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

优化复杂MySQL查询 降低campaign调研响应数据导出查询耗时

MySQL查询优化方案

问题核心根源

  • 原查询使用了6个关联子查询,外层每返回1行就要执行6次子查询,limit 500就累计执行3000次查询,全量5万行时会执行30万次,是耗时过高的核心原因
  • 现有索引仅为单列索引,无法覆盖查询所需的过滤、排序、返回字段,每次查询都需要回表读取数据,IO成本极高
  • 表使用MyISAM存储引擎,查询时会触发表锁,阻塞其他读写操作,且缓存效率远低于InnoDB

优化方案

1. 新增覆盖索引(成本最低,见效最快)

给两张表新增联合覆盖索引,所有查询逻辑可直接通过索引完成,无需回表:

-- 响应表覆盖索引,包含所有过滤、分组、排序、返回字段
ALTER TABLE campaign_survey_responses ADD INDEX idx_campaign_msisdn_question(campaign_id, msisdn, question_id, answer);
-- 问题表覆盖索引,包含关联、过滤字段
ALTER TABLE campaign_survey_questions ADD INDEX idx_parent_sort(id, parent_id, sort_order);

2. 改写SQL为条件聚合(核心优化)

将关联子查询改写为单次扫描+条件聚合,仅需扫描1次响应表即可完成所有计算,性能提升至少10倍以上:

SELECT 
    a.msisdn,
    GROUP_CONCAT(CASE WHEN a.question_id = 14750 THEN COALESCE(a.answer, 'skip') END ORDER BY a.question_id SEPARATOR ',') AS answer,
    GROUP_CONCAT(CASE WHEN a.question_id = 14751 THEN COALESCE(a.answer, 'skip') END ORDER BY a.question_id SEPARATOR ',') AS a1,
    GROUP_CONCAT(CASE WHEN q.parent_id = 5128 AND q.sort_order = 0 THEN COALESCE(a.answer, 'skip') END ORDER BY a.question_id SEPARATOR ',') AS sur1,
    GROUP_CONCAT(CASE WHEN a.question_id = 14768 THEN COALESCE(a.answer, 'skip') END ORDER BY a.question_id SEPARATOR ',') AS a2,
    GROUP_CONCAT(CASE WHEN q.parent_id = 5108 AND q.sort_order = 0 THEN COALESCE(a.answer, 'skip') END ORDER BY a.question_id SEPARATOR ',') AS sur2,
    GROUP_CONCAT(CASE WHEN a.question_id = 14785 THEN COALESCE(a.answer, 'skip') END ORDER BY a.question_id SEPARATOR ',') AS a3,
    GROUP_CONCAT(CASE WHEN q.parent_id = 5148 AND q.sort_order = 0 THEN COALESCE(a.answer, 'skip') END ORDER BY a.question_id SEPARATOR ',') AS sur3
FROM campaign_survey_responses a
-- 过滤出仅包含父问题14750响应的用户,和原查询逻辑完全一致
INNER JOIN (
    SELECT DISTINCT msisdn 
    FROM campaign_survey_responses 
    WHERE campaign_id = 11559 AND question_id = 14750
) filter ON a.msisdn = filter.msisdn
LEFT JOIN campaign_survey_questions q ON a.question_id = q.id
WHERE a.campaign_id = 11559
GROUP BY a.msisdn
LIMIT 500;

3. 存储引擎优化

将表从MyISAM改为InnoDB,解决表锁问题,同时利用InnoDB的缓冲池提升热点数据的查询效率:

ALTER TABLE campaign_survey_responses ENGINE=InnoDB;

4. 导出效率优化

如果仅需要导出CSV文件,直接使用MySQL原生导出语法,避免应用层数据传输开销,导出全量5万行仅需几秒:

SELECT 
    a.msisdn,
    GROUP_CONCAT(CASE WHEN a.question_id = 14750 THEN COALESCE(a.answer, 'skip') END ORDER BY a.question_id SEPARATOR ',') AS answer,
    GROUP_CONCAT(CASE WHEN a.question_id = 14751 THEN COALESCE(a.answer, 'skip') END ORDER BY a.question_id SEPARATOR ',') AS a1,
    GROUP_CONCAT(CASE WHEN q.parent_id = 5128 AND q.sort_order = 0 THEN COALESCE(a.answer, 'skip') END ORDER BY a.question_id SEPARATOR ',') AS sur1,
    GROUP_CONCAT(CASE WHEN a.question_id = 14768 THEN COALESCE(a.answer, 'skip') END ORDER BY a.question_id SEPARATOR ',') AS a2,
    GROUP_CONCAT(CASE WHEN q.parent_id = 5108 AND q.sort_order = 0 THEN COALESCE(a.answer, 'skip') END ORDER BY a.question_id SEPARATOR ',') AS sur2,
    GROUP_CONCAT(CASE WHEN a.question_id = 14785 THEN COALESCE(a.answer, 'skip') END ORDER BY a.question_id SEPARATOR ',') AS a3,
    GROUP_CONCAT(CASE WHEN q.parent_id = 5148 AND q.sort_order = 0 THEN COALESCE(a.answer, 'skip') END ORDER BY a.question_id SEPARATOR ',') AS sur3
FROM campaign_survey_responses a
INNER JOIN (
    SELECT DISTINCT msisdn 
    FROM campaign_survey_responses 
    WHERE campaign_id = 11559 AND question_id = 14750
) filter ON a.msisdn = filter.msisdn
LEFT JOIN campaign_survey_questions q ON a.question_id = q.id
WHERE a.campaign_id = 11559
GROUP BY a.msisdn
INTO OUTFILE '/var/lib/mysql-files/survey_result.csv'
FIELDS TERMINATED BY ',' ENCLOSED BY '"'
LINES TERMINATED BY '\n';

注意MySQL 5.7默认仅允许导出到/var/lib/mysql-files目录,需确保该目录存在且MySQL有写入权限。

额外优化建议

如果该导出任务是定期执行的,可以新增一张汇总表,用定时任务提前将聚合结果写入汇总表,导出时直接查询汇总表即可,响应时间可以降到毫秒级。

内容的提问来源于stack exchange,提问作者Asfandyar Khan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 12:36:04