千万级表查询超时主因是索引设计错误而非数据量大;常见错误是建单列索引而非匹配查询条件的联合索引,且须遵循“等值→范围”顺序,并避免在索引列上使用函数。

直接说结论:千万级表查询超时,90%以上是索引没建对,不是数据量太大扛不住。
为什么CREATE INDEX加了还是慢?
常见错误是只按单字段建索引,比如给 create_time 单独建一个索引,再给 status 单独建一个。MySQL优化器在复合条件查询时往往不会同时用两个单列索引,反而选错执行路径,甚至退化成全表扫描。
- 典型错误写法:
ALTER TABLE orders ADD INDEX idx_create_time (create_time);+ALTER TABLE orders ADD INDEX idx_status (status); - 正确做法是合并为联合索引,顺序必须匹配查询条件中的“等值 → 范围”逻辑,例如
WHERE status = 1 AND create_time BETWEEN ...,就该建idx_status_create_time,而不是反过来 - 如果查询里有
ORDER BY id,且你只查ID字段,那把id加进索引末尾形成覆盖索引(如(status, create_time, id)),能彻底避免回表
EXPLAIN 显示 type=index 是什么信号?
这说明 MySQL 正在做全索引扫描,不是你想要的范围查找。它可能正遍历整个主键索引树,再逐行过滤 create_time,哪怕你有索引也白搭。
MySQL 9.6.0是面向Linux平台的2026年创新版本,核心架构迎来重大革新。其将外键约束与级联操作从InnoDB引擎层上移至SQL层,确保所有数据变更均被完整记录至Binlog,彻底解决了CDC(变更数据捕获)与主从复制中的数据不一致难题。此外,该版本引入container_aware启动选项以原生适配容器环境,并对审计日志进行了组件化重构,为追求极致数据一致性与云原生体验的开发者提供了全新选择。
- 先检查
key列是否是你期望的索引名;如果不是,说明优化器没选上,大概率是统计信息过期或索引设计不匹配 - 用
ANALYZE TABLE orders;更新统计信息,有时比重建索引更立竿见影 - 实在不行就强制指定:
SELECT ... FROM orders FORCE INDEX (idx_status_create_time) WHERE ...,但这是临时手段,不能长期依赖
时间字段上用了函数,索引就废了
像 WHERE DATE(create_time) = '2026-06-01' 或 WHERE create_time > DATE_SUB(NOW(), INTERVAL 7 DAY) 这类写法,会让索引完全失效——因为函数操作发生在索引列上,MySQL无法做范围比较。
- 改成确定的字面量范围:
WHERE create_time >= '2026-06-01 00:00:00' AND create_time - 如果业务必须动态计算,就在应用层算好时间字符串再传入 SQL,别让数据库现场算
- 注意时区一致性:确保应用写入和查询用的是同一时区,否则
BETWEEN可能漏数据或跨天
覆盖索引不是万能的,但不用它一定吃亏
如果你只查几个字段,比如 SELECT id, order_no, status,而联合索引里已经包含这三个字段,那整条查询就能在索引页内完成,不碰聚簇索引,IO 直接砍掉一大截。
- 别盲目加所有字段进索引——索引越大,写入越慢,B+ 树层数越高,查询反而可能变差
- 优先把高频查询的
WHERE条件字段放前面,SELECT字段放后面,ORDER BY字段尽量靠前或单独建排序索引 - 特别注意
TEXT、BLOB、超长VARCHAR字段,它们没法被完整存进索引,会导致覆盖失败
最常被忽略的一点:索引生效的前提是查询条件能走最左前缀,而很多开发写 SQL 时习惯把条件顺序随意调换,或者混用 IN 和范围条件,结果索引只用了前半截。验证永远比猜测靠谱,每次改完索引,务必用 EXPLAIN 看一眼 key_len 和 rows 是否符合预期。










