MySQL 5.7为何未用index_merge反而全表扫描?强制索引无效
问题
统计home_user表(约5万行)中每个用户的总donate_count,关联donate_info表(约10万行)时,即使添加FORCE INDEX提示指定user_id_idx和ut_idx索引,MySQL仍未使用index_merge,反而执行全表扫描,指定索引也未生效,想了解原因。
执行的SQL语句
SELECT COUNT(DISTINCT `hu`.`id`) AS aggregate, COUNT(`di`.`id`) AS donate_count FROM `home_user` AS `hu` LEFT JOIN `donate_info` AS `di` FORCE INDEX (user_id_idx, ut_idx) ON ( `di`.`user_id` = `hu`.`id` OR ( `di`.`type` = 'proxy' and `di`.`user_mobile` = `hu`.`account` ) );
donate_info表索引信息
KEY `user_id_idx` (`user_id`) KEY `ut_idx` (`user_mobile`,`type`)
原因分析
- 跨表OR条件限制index_merge触发:MySQL的index_merge优化仅针对单表查询的OR条件,你这里是在
LEFT JOIN的关联条件里用OR连接了两个跨表匹配逻辑(di.user_id=hu.id和di.user_mobile=hu.account),这种跨表的OR组合无法触发索引合并,优化器找不到高效的索引关联方式,只能走全表扫描。 - FORCE INDEX无法突破成本判断:
FORCE INDEX只是给优化器一个优先选择索引的提示,但优化器会评估使用索引的实际成本。这里因为是跨表OR关联,即使指定索引,优化器也无法将两个索引的检索结果和home_user的行做高效匹配,判断使用索引的成本反而高于全表扫描,所以直接忽略了索引提示。 - 索引与关联逻辑不匹配:
ut_idx是(user_mobile, type)的联合索引,虽然单条件下能匹配type='proxy' AND user_mobile=hu.account,但因为和另一个跨表条件用OR连接,优化器无法单独用该索引检索后,再和user_id_idx的结果合并做关联。
优化方案
- 拆分查询后合并结果:把OR条件拆成两个独立的
LEFT JOIN,再通过UNION ALL合并统计结果,这样两个子查询可以分别用到指定的索引:
SELECT COUNT(DISTINCT hu.id) AS aggregate, SUM(donate_count) AS donate_count FROM ( SELECT hu.id, COUNT(di.id) AS donate_count FROM home_user hu LEFT JOIN donate_info di FORCE INDEX(user_id_idx) ON di.user_id = hu.id GROUP BY hu.id UNION ALL SELECT hu.id, COUNT(di.id) AS donate_count FROM home_user hu LEFT JOIN donate_info di FORCE INDEX(ut_idx) ON di.type = 'proxy' AND di.user_mobile = hu.account GROUP BY hu.id ) t;
- 调整业务数据结构:如果业务允许,可在
donate_info中补充冗余字段(比如把proxy类型记录的user_id补全),或者统一关联逻辑,让关联条件能匹配单一索引,避免跨表OR的情况。
内容的提问来源于stack exchange,提问作者jmz
相关产品推荐
相关产品推荐

