覆盖索引指查询所需字段全部包含在索引中,避免回表;联合索引按最左前缀原则排序,高频等值列置左、范围查询列置右,二者协同设计以减少I/O、免排序、少回表。

覆盖索引和联合索引不是“选一个用”,而是根据查询模式协同设计的两种关键手段。真正提升性能的关键,在于让数据库尽量少读数据页、少回表、少排序——索引就是为这个目标服务的。
什么是覆盖索引?为什么它快?
覆盖索引指:查询所需的所有字段,全部包含在索引的键列(或包含列)中,无需回表查聚簇索引。MySQL 的二级索引叶子节点存的是主键值,如果 SELECT 的列都在该索引里,引擎直接从索引树返回结果,跳过聚簇索引查找。
例如:
SELECT user_id, status FROM orders WHERE order_date > '2024-01-01';
若存在索引 INDEX idx_date_status (order_date, status),但没包含 user_id,则仍需回表取 user_id;若建为 INDEX idx_cover (order_date, status, user_id),就构成覆盖索引。
- 覆盖索引减少 I/O:避免额外的聚簇索引随机读
- 适用于高频、轻量、只读场景(如状态列表、统计摘要)
- 注意:过多包含列会增大索引体积,影响写入和缓存效率
联合索引怎么排序?最左前缀到底怎么用?
联合索引按定义顺序构建 B+ 树:先按第一列排序,相同时再按第二列,依此类推。因此,只有满足“连续且从左开始”的列条件,才能高效利用索引进行范围扫描或等值查找。
对于索引 INDEX idx_u_s_t (user_id, status, create_time):
- ✅ WHERE user_id = 100 AND status = 'paid' → 可走索引前两列
- ✅ WHERE user_id = 100 ORDER BY create_time → 索引天然有序,免排序
- ❌ WHERE status = 'paid' → 跳过首列,无法使用该索引(除非是 MySQL 8.0+ 的跳跃扫描,但有前提)
- ⚠️ WHERE user_id > 100 AND status = 'paid' → status 列失效,仅用于过滤,不参与范围扫描
怎么设计高效的联合索引?三个实操原则
别靠猜,看执行计划(EXPLAIN)+ 查询频率 + 数据分布。重点考虑三件事:
- 高频等值条件放最左:如 user_id 出现在 90% 查询中,就优先作为第一列
- 范围查询列只放最后:>、>=、create_time 放末尾,前面可接多个等值列
- 把排序/分组字段纳入索引末尾:如常查 WHERE user_id = ? AND status = ? ORDER BY update_time,索引应为 (user_id, status, update_time),避免 filesort
覆盖索引 + 联合索引组合优化示例
业务场景:运营后台查“某用户近30天已完成订单数及总金额”
SQL:
SELECT COUNT(*), SUM(amount) FROM orders
WHERE user_id = 123
AND status = 'completed'
AND create_time >= '2024-05-01';
最优索引:
INDEX idx_user_status_time_amt (user_id, status, create_time, amount)
说明:
- 前三列满足最左前缀:精确过滤 user_id + status + 时间范围
- amount 加入索引末尾,使聚合函数 SUM(amount) 直接走索引,无需回表
- COUNT(*) 也受益:引擎遍历索引即可计数,不访问数据行
- 注意:不要加主键 ID —— 这里根本不需要它,徒增索引大小










