MySQL 8.0.29查询未走索引,全表扫描问题排查求助
MySQL 8.0.29全表扫描未选用索引问题分析
问题场景
使用MySQL 8.0.29版本,执行以下查询时触发全表扫描,未选用已创建的索引,寻求原因及解决方法。
查询语句
select journey_level, src_screen_name from_entity_name, src_screen_id from_entity_id, dest_screen_name to_entity_name, dest_screen_id to_entity_id, dest_screen_id to_entity_id_list, concat(journey_level, '-', src_screen_id, '-', src_screen_name) from_entity, concat(journey_level + 1, '-', dest_screen_id, '-', dest_screen_name) to_entity, count(1) cnt, sum(ifnull(is_crash, 0)) crash_cnt, sum(ifnull(is_anr, 0)) anr_cnt, sum(ifnull(is_rage, 0)) rage_cnt, case dest_screen_id when -1 then 'Red' else '' end node_color from ue_summary.user_journey_screens_1943 s where first_session_created_at between '2022-09-17 00:00:00' and '2022-10-17 10:42:35' and s.platform = '1' and journey_level <= '4' group by journey_level, from_entity_id, from_entity_name, to_entity_id, to_entity_name;
执行计划
*************************** 1. row *************************** id: 1 select_type: SIMPLE table: s partitions: NULL type: ALL possible_keys: date_platform_level,platform_date key: NULL key_len: NULL ref: NULL rows: 793512853 filtered: 1.67 Extra: Using where; Using temporary
表结构
CREATE TABLE `user_journey_screens_1943` ( `id` bigint NOT NULL AUTO_INCREMENT, `app_id` int DEFAULT NULL, `platform` tinyint DEFAULT NULL, `asi` bigint DEFAULT NULL, `first_session_created_at` datetime DEFAULT NULL, `user_task_id` bigint DEFAULT NULL, `app_version_id` int DEFAULT NULL, `journey_level` int DEFAULT NULL, `src_screen_id` bigint DEFAULT NULL, `src_screen_name` varchar(300) DEFAULT NULL, `src_capture_time` bigint DEFAULT NULL, `src_capture_time_relative` bigint DEFAULT NULL, `dest_screen_id` bigint DEFAULT NULL, `dest_screen_name` varchar(300) DEFAULT NULL, `dest_capture_time` bigint DEFAULT NULL, `dest_capture_time_relative` bigint DEFAULT NULL, `is_crash` tinyint DEFAULT NULL, `is_anr` tinyint DEFAULT NULL, `is_rage` tinyint DEFAULT NULL, PRIMARY KEY (`id`), KEY `app_id_date_level` (`app_id`,`first_session_created_at`,`journey_level`), KEY `asi` (`asi`), KEY `date_platform_level` (`first_session_created_at`,`platform`,`journey_level`), KEY `platform_date` (`platform`,`first_session_created_at`) ) ENGINE=InnoDB AUTO_INCREMENT=1937612717 DEFAULT CHARSET=latin1
索引未被选用的原因
列类型不匹配触发隐式转换
查询中journey_level <= '4',但表中journey_level是int类型,字符串与数字的比较会触发隐式类型转换,破坏索引的有序性,导致MySQL无法利用索引中journey_level列的过滤能力。现有索引覆盖度不足
现有索引仅包含过滤条件相关的列,但查询需要的分组字段(src_screen_id、src_screen_name等)和聚合计算字段(is_crash、is_anr等)都不在索引中。如果走索引,MySQL需要频繁回表读取这些字段,对于7.9亿行的表来说,回表开销远大于全表扫描,优化器因此放弃使用索引。优化器成本预估判断
执行计划显示filtered仅为1.67%,预估符合条件的行数约1300万,但MySQL认为走索引后回表的总成本高于全表扫描,最终选择全表扫描。
解决方案
修正类型不匹配问题
将查询中的journey_level <= '4'改为journey_level <= 4,避免隐式转换,确保索引能正常发挥过滤作用。创建覆盖索引
针对查询的过滤、分组、聚合需求,创建包含所有必要字段的联合索引,让MySQL无需回表即可完成查询:
CREATE INDEX idx_platform_date_level_cover ON ue_summary.user_journey_screens_1943 ( platform, first_session_created_at, journey_level, src_screen_id, src_screen_name, dest_screen_id, dest_screen_name, is_crash, is_anr, is_rage );
索引列顺序遵循最左前缀原则:先放等值过滤的platform,再放范围过滤的first_session_created_at,接着是过滤条件journey_level,最后是分组和聚合需要的字段,确保索引覆盖整个查询流程。
- 验证索引效果
创建索引后,执行EXPLAIN查看执行计划,确认type列变为range或ref,key列显示新创建的索引,说明索引已被有效利用。
内容的提问来源于stack exchange,提问作者RRQ
相关产品推荐
相关产品推荐

