mysql 5.7千万级表索引优化核心是精准匹配查询模式,须绕开不支持降序索引、无直方图、最左前缀严格、统计不准等硬伤;explain显示用索引仍慢,主因是range/ref扫描行数过多、key_len过小、using filesort/ temporary、函数导致失效或排序方向不匹配。

MySQL 5.7 对千万级数据做索引优化,核心不是“多建索引”,而是让每个索引都精准匹配查询模式——尤其要绕开 5.7 的几个硬伤:不支持降序索引、无直方图、最左前缀匹配极其严格、统计信息不准容易选错索引。
为什么 EXPLAIN 显示用了索引,但查询还是慢?
这是 MySQL 5.7 千万级表最典型的幻觉。常见原因有:
-
type是range或ref,但rows值高达百万级——说明索引扫了太多行,本质仍是“伪索引扫描” -
key_len比预期小(比如联合索引(a,b,c),key_len只显示 a 的长度),说明只有最左列生效,b/c 被忽略 -
Extra出现Using filesort或Using temporary——5.7 无法用索引直接满足ORDER BY或GROUP BY,尤其当含DESC时(它会无视ORDER BY x DESC,仍按升序走) - WHERE 中用了函数或表达式,如
WHERE DATE(create_time) = '2023-01-01',直接导致索引失效
复合索引字段顺序怎么排才不踩坑?
别信“高频字段放左边”的模糊说法。在 5.7 里,顺序必须严格按查询条件的**过滤强度 + 排序需求**来定:
- 等值查询字段(
=或IN)优先,且把区分度高的放最左(比如status只有 3 个值,user_id百万级唯一,那user_id应更靠前) - 范围查询字段(
>,BETWEEN,LIKE 'abc%')只能放在等值字段之后,且最多一个——再往后字段全失效 - 如果查询带
ORDER BY a ASC, b ASC,索引必须是(a, b, ...);若写成ORDER BY a DESC, b ASC,5.7 会放弃索引排序,强制Using filesort - 避免把
TEXT或长VARCHAR放索引里;要用前缀索引,先跑SELECT COUNT(DISTINCT LEFT(title, 12))/COUNT(*) FROM article看区分度是否 > 0.95
哪些索引绝对不能建?
5.7 写入压力敏感,无效索引比没索引更危险:
- 单字段索引和联合索引字段重复,比如已有
INDEX idx_uid_status (user_id, status),再单独建INDEX idx_user_id (user_id)——后者完全冗余 - 只用于
SELECT *但 WHERE 条件从不涉及的字段,建了也白建,还拖慢INSERT -
ENUM或低基数字段(如is_deleted TINYINT)单独建索引,5.7 优化器大概率不走,还浪费空间 - 包含
NULL值的字段建索引需谨慎:WHERE col IS NULL能走索引,但WHERE col != 'x'会跳过NULL行,且统计信息常不准,易误判
覆盖索引怎么写才真生效?
5.7 的覆盖索引必须“查什么,索引里就有什么”,连隐式字段都不能漏:
- 假设查
SELECT id, name, created_at FROM user WHERE age > 25 ORDER BY created_at DESC,5.7 不支持created_at DESC索引,所以得改成ORDER BY created_at ASC,然后索引定义为INDEX idx_age_created_id_name (age, created_at, id, name) - InnoDB 主键自动包含在二级索引中,所以
id在联合索引里可省略,但显式写出更清晰(如(age, created_at, name)已隐含id) - 如果查询中有
SELECT COUNT(*)且带 WHERE,确保索引能覆盖 WHERE 条件——否则仍要回表计数 - 验证是否覆盖:EXPLAIN 后看
Extra是否出现Using index,而不是Using index condition(后者说明仍需回表)
5.7 的索引优化,本质是和优化器“斗智斗勇”:它不会猜你想要什么,只会机械匹配最左前缀;它看不到数据分布细节,全靠过时的统计信息;它对 DESC 和函数毫无办法。所以每建一个索引,都要用真实数据量跑 EXPLAIN FORMAT=JSON,盯着 rows、used_columns 和 filesort_priority_queue_optimization 这些字段——而不是只看 key 列有没有名字。











