添加条件后SQL查询从262ms骤增至28s,原因何在?
为何添加
AND p.site_id = 3后SQL查询耗时剧增? 原始查询语句
SELECT DATE(ar.created_at) AS my_date, COUNT(*) AS requests, SUM(ar.is_scrubbed) AS scrubbed_requests, SUM(ar.has_state_mismatch) AS state_mismatch_total, AVG(ar.special_result) AS special_avg FROM advert_requests ar LEFT JOIN placements p ON p.id = ar.placement_id WHERE ar.created_at >= '2022-11-22' AND ar.created_at < '2022-11-25' -- 为何添加该条件后耗时剧增? AND p.site_id = 3 AND ar.debug = 0 GROUP BY my_date ORDER BY my_date DESC;
查询耗时对比
- 移除
AND p.site_id = 3条件时:查询耗时约200-300ms,对应的执行计划截图:
- 添加
AND p.site_id = 3条件后:查询耗时在4秒至28秒之间,对应的执行计划截图:
原因分析
LEFT JOIN 逻辑转为 INNER JOIN
原本的LEFT JOIN会保留advert_requests中所有符合条件的行,哪怕没有匹配的placements记录。但添加p.site_id = 3后,WHERE子句中对p表的非NULL约束会自动将查询转为INNER JOIN——只有同时匹配advert_requests行和placements中site_id=3行的记录才会被保留,彻底改变了查询的执行逻辑。执行计划的效率差异
- 移除
p.site_id条件时:优化器可以利用advert_requests表上的created_at+debug联合索引,快速筛选出时间范围内且debug=0的行。由于查询不需要用到placements表的任何字段,优化器甚至会直接跳过JOIN操作,直接对过滤后的行进行聚合,这是耗时极短的核心原因。 - 添加
p.site_id条件后:优化器必须先处理placements表的筛选和关联逻辑。如果placements表的site_id字段没有建立索引,会导致全表扫描;如果advert_requests表的placement_id字段没有索引,关联操作(比如嵌套循环)会异常低效。此外,若site_id=3对应的placement数量庞大,关联后的数据量大幅增加,后续GROUP BY聚合操作的成本也会显著上升。
- 移除
统计信息偏差导致执行计划不稳定
查询耗时波动大(4-28秒),大概率是因为MySQL的表统计信息过时。优化器错误估计了site_id=3对应的行数,时而选择低效的嵌套循环连接,时而选择相对高效的哈希连接,导致耗时差异巨大。
内容的提问来源于stack exchange,提问作者James Mills
相关产品推荐
相关产品推荐

