联合索引不是把常用列随便放在一起。列顺序要服务具体查询:通常先处理等值条件,再考虑范围和排序,最后只在收益明确时加入少量返回列形成覆盖。
Table of contents
从一条查询开始
SELECT id, created_at
FROM orders
WHERE user_id = 1
AND created_at >= '2026-01-01'
ORDER BY created_at, id
LIMIT 20;
实验索引是 (user_id, created_at, id)。user_id 做等值定位,后续键仍按 created_at, id 有序,可以完成范围与排序;查询只返回索引已包含的列时,还可能覆盖读取。
最左前缀不是口诀
B+Tree 按整个复合键的字典序排列。没有约束第一列时,相同第二列散落在许多第一列分组中,所以通常无法像完整前缀那样连续定位。它是数据排列结果,不是数据库人为设置的神秘规则。
范围条件之后的列不一定完全“失效”:它们可能不能继续缩小索引起止区间,却仍可参与索引条件下推或排序判断。应以 EXPLAIN ANALYZE 为准。
覆盖与下推的区别
- 覆盖索引:所需列都在索引里,无需回表取完整行。
- 索引条件下推(ICP):存储引擎在回表前,用索引中的额外条件先过滤,减少回表次数。
覆盖是“不要回表”,下推是“少回表”。二者都不是免费的:索引越宽,写入维护成本、占用空间和 Buffer Pool 压力越大。
一张查询—索引清单
每个候选索引都记录服务的 Top SQL、过滤列、排序列、预计选择性、写入频率、索引大小和可替代索引。发现一个宽索引能覆盖另一个窄索引时,也不要直接删除,先用不可见索引或测试环境观察计划变化。
下一步
继续阅读06-08 排序、分页、Join 与子查询优化,处理跨表和深分页问题。