You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

生产环境MySQL左连接查询超时问题排查与优化求助

MySQL查询超时优化方案

问题背景

生产环境中一条左连接查询超时,拆分后第二个统计查询首次执行耗时6秒(后续因缓存降至300ms),涉及contact_tag表的分组统计操作,相关查询语句及表结构如下:

原左连接查询语句

SELECT
    `tags`.*,
    COUNT( ct.contact_id ) AS contacts_count 
FROM
    `tags`
    LEFT JOIN `contact_tag` AS `ct` ON `tags`.`id` = `ct`.`tag_id` 
WHERE
    `tags`.`company_id` = 1068 
    AND `tags`.`category` = 1 
    AND `tags`.`deleted_at` IS NULL 
GROUP BY
    `tags`.`id` 
ORDER BY
    `tags`.`id` DESC

拆分后的统计查询语句

SELECT 
   tag_id, 
   COUNT(com_tag.contact_id) AS communications_count
FROM 
   contact_tag as com_tag
WHERE 
   tag_id IN (294610,286349,286333,286332,286331,286330,268187,268175,265225,265224,265223,260136,257287)
GROUP BY 
   tag_id;

contact_tag表结构定义

CREATE TABLE `contact_tag` (
    `id` bigint unsigned NOT NULL AUTO_INCREMENT,
    `contact_id` int unsigned NOT NULL,
    `tag_id` int unsigned NOT NULL,
    `created_at` timestamp NULL DEFAULT NULL,
    `updated_at` timestamp NULL DEFAULT NULL,
    PRIMARY KEY (`id`),
    UNIQUE KEY `contact_tag_contact_id_tag_id_unique` (`contact_id`,`tag_id`),
    KEY `contact_tag_tag_id_foreign` (`tag_id`),
    CONSTRAINT `contact_tag_contact_id_foreign` FOREIGN KEY (`contact_id`) REFERENCES `contacts` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
    CONSTRAINT `contact_tag_tag_id_foreign` FOREIGN KEY (`tag_id`) REFERENCES `tags` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=2477082 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci

优化方案

1. 创建覆盖索引,消除回表开销

当前查询依赖contact_tag_tag_id_foreign索引(仅包含tag_id),统计contact_id时需要回表读取数据,导致磁盘IO耗时高。创建联合覆盖索引:

ALTER TABLE `contact_tag` ADD INDEX `idx_tag_id_contact_id` (`tag_id`, `contact_id`);

该索引直接包含查询所需的tag_id和contact_id字段,MySQL无需回表即可完成统计,大幅降低首次执行的磁盘IO开销。

2. 优化tags表的过滤与排序效率

原查询中tags表的过滤条件为company_id=1068、category=1、deleted_at IS NULL,并按id DESC排序。创建复合索引优化过滤与排序逻辑:

ALTER TABLE `tags` ADD INDEX `idx_company_category_deleted_id` (`company_id`, `category`, `deleted_at`, `id` DESC);

此索引可直接定位符合条件的标签数据,同时利用索引顺序完成ORDER BY操作,避免额外的文件排序,提升原左连接查询的整体效率。

3. 调整查询逻辑,减少关联数据量(可选)

若无需拆分查询,可将原语句改写为先过滤标签再关联统计的形式,结合上述索引进一步优化:

SELECT
    t.*,
    COALESCE(ct.contacts_count, 0) AS contacts_count
FROM (
    SELECT id, name, [其他需要返回的tags字段]
    FROM `tags`
    WHERE `company_id` = 1068 
      AND `category` = 1 
      AND `deleted_at` IS NULL
    ORDER BY id DESC
) t
LEFT JOIN (
    SELECT tag_id, COUNT(contact_id) AS contacts_count
    FROM `contact_tag`
    GROUP BY tag_id
) ct ON t.id = ct.tag_id
ORDER BY t.id DESC;

子查询先过滤出目标标签数据,再关联统计结果,减少关联的数据量,配合索引可避免全表扫描。

4. 优化InnoDB缓冲池配置

首次执行慢通常是因为数据未加载到内存缓存中,调整InnoDB缓冲池大小(建议设置为服务器内存的50%-70%):

# my.cnf 配置示例
innodb_buffer_pool_size = 8G

重启MySQL后,常用数据会被缓存到内存,后续查询(包括冷启动后的首次查询)耗时将显著降低。

内容的提问来源于stack exchange,提问作者Juan Pablo B

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 09:35:16