单个子查询运行快但多个组合导致MySQL挂起,无新增索引权限如何修复?
优化方案
问题根因
MySQL优化器对多层嵌套IN子查询的处理逻辑存在缺陷,当存在2个及以上IN子查询时,会将查询转换为外层逐行匹配的嵌套循环,相当于外层每遍历1条记录就要重复执行多次子查询,时间复杂度直接翻倍,最终出现无响应问题。
推荐优化写法
方案1:按周分组统计(写法简洁)
仅需扫描1次posts表即可完成统计,无需新增索引:
SELECT user_id FROM `posts` -- 先筛选过去4周的所有帖子,缩小扫描范围 WHERE post_date > (UNIX_TIMESTAMP() - 604800 * 4) AND post_date <= UNIX_TIMESTAMP() GROUP BY user_id -- 统计用户发布内容覆盖的不同周数,等于4即满足每周至少发布1次 HAVING COUNT(DISTINCT FLOOR((UNIX_TIMESTAMP() - post_date) / 604800)) = 4;
方案2:按周位判断(性能更优)
如果posts表数据量极大,可以用条件判断替代DISTINCT,进一步降低计算开销:
SELECT user_id FROM `posts` WHERE post_date > (UNIX_TIMESTAMP() - 604800 * 4) AND post_date <= UNIX_TIMESTAMP() GROUP BY user_id HAVING -- 判断每周是否至少有1条发布记录 MAX(CASE WHEN post_date > UNIX_TIMESTAMP() - 604800 THEN 1 ELSE 0 END) = 1 AND MAX(CASE WHEN post_date > UNIX_TIMESTAMP() - 604800*2 AND post_date <= UNIX_TIMESTAMP() - 604800 THEN 1 ELSE 0 END) = 1 AND MAX(CASE WHEN post_date > UNIX_TIMESTAMP() - 604800*3 AND post_date <= UNIX_TIMESTAMP() - 604800*2 THEN 1 ELSE 0 END) = 1 AND MAX(CASE WHEN post_date > UNIX_TIMESTAMP() - 604800*4 AND post_date <= UNIX_TIMESTAMP() - 604800*3 THEN 1 ELSE 0 END) = 1;
优化逻辑说明
- 两种写法都将多次表扫描降低为1次表扫描,完全规避了多层IN子查询带来的嵌套循环开销
- 优先走post_date字段的范围扫描,如果你原有post_date已经有索引,查询速度会更快
- 不需要新增任何额外索引,完全符合场景限制
内容的提问来源于stack exchange,提问作者ProgrammerGirl
相关产品推荐
相关产品推荐

