当前位置: 首页 > 新闻动态 > 网络资讯

复合索引顺序写错导致全表扫描的经典案例

作者:冰川箭仙 浏览: 发布日期:2026-01-30
[导读]:联合索引必须遵循最左前缀原则,跳过最左列将导致全表扫描;范围查询后右侧列失效;ORM动态SQL易错序;字符集或类型不一致会隐式转换使索引失效;索引顺序应按查询频次、过滤强度和等值条件优化。
联合索引必须遵循最左前缀原则,跳过最左列将导致全表扫描;范围查询后右侧列失效;ORM动态SQL易错序;字符集或类型不一致会隐式转换使索引失效;索引顺序应按查询频次、过滤强度和等值条件优化。

联合索引没按最左前缀查,直接退化*表扫描

复合索引不是“只要字段在里面就能用”,它像字典——先按第一列排,相同再按第二列,以此类推。一旦 WHERE 条件跳过最左列(比如索引是 (name, age, city),却只写 WHERE age = 25),MySQL 就没法从索引树根开始定位,只能扫全表。

  • 典型错误:SELECT * FROM users WHERE age > 20 AND city = '杭州',而索引是 KEY idx_name_age_city (name, age, city)
  • 真正生效的写法必须包含最左列:WHERE name = '张三' AND age > 20(此时 city 虽未用上,但 nameage 都走索引)
  • 如果业务真要高频查 age,别硬凑,单独建 INDEX idx_age (age) 更实在

范围查询后,右边的索引列自动“失能”

在联合索引中,一旦某列用了 >BETWEEN 这类范围条件,它右边所有列就只能回表过滤,不再参与索引查找。

  • 例子:索引为 (status, create_time, user_id),执行 WHERE status = 'paid' AND create_time > '2025-01-01'user_id 完全不走索引
  • 想让 user_id 也进索引?把等值列往前放:INDEX (status, user_id, create_time),再查 WHERE status = 'paid' AND user_id = 123 AND create_time > '2025-01-01'
  • 注意:MySQL 8.0.13+ 支持“索引跳跃扫描”(Loose Index Scan),但只在最左列高区分度时可能触发,不能当稳定方案依赖

ORM 拼 SQL 时字段顺序错位,人眼难发现

很多开发者用 MyBatis、JPA 等框架,写动态 SQL 或 QueryDSL 时,条件顺序由代码逻辑控制。稍不注意,name 条件被 if 判断跳过,只剩 agecity,复合索引瞬间失效。

  • 排查方法:在日志里开 show_sql=true + log4j.level.com.xxx.mapper=DEBUG,抓出真实执行的 SQL,再用 EXPLAIN 验证
  • 安全做法:对高频组合查询,宁可多建一个覆盖索引,比如 INDEX idx_age_city (age, city),也不强求复用主复合索引
  • 特别警惕分页场景:ORDER BY age LIMIT 10 却没 WHERE name = ? —— 即使有 (name, age) 索引,排序也无法利用

字段类型或字符集不一致,悄悄让最左列“失效”

你以为写了最左列,但 MySQL 在底层做了隐式转换:比如索引列是 utf8mb4_bin,而参数传的是 utf8mb4_general_ci;或者字段是 VARCHAR,你传了数字没加引号——这时最左列虽然出现,但匹配过程已绕过索引结构。

  • 常见表现:EXPLAIN 显示 type=ALLkey=NULL,但 WHERE 里明明有索引第一列
  • 验证方式:执行 SHOW CREATE TABLE xxx 查字段定义,再对比应用层传参的类型和字符集
  • 修复动作:统一字符集(推荐全库 utf8mb4_unicode_ci),字符串参数务必加单引号,Java 侧用 String 接收而非 Long

复合索引顺序不是“按业务语义排”,而是按查询频次+过滤强度+是否等值来

排。最左列选错,后面全白搭;更麻烦的是,这种问题在线上跑几天都未必暴露——数据量小的时候全表扫描也快,一到大促就卡死。

免责声明:转载请注明出处:http://jing-feng.com.cn/news/767357.html

扫一扫高效沟通

多一份参考总有益处

免费领取网站策划SEO优化策划方案

请填写下方表单,我们会尽快与您联系
感谢您的咨询,我们会尽快给您回复!