返回
2026.07.25 ARTICLE

MySQL 联合索引怎么设计:从查询条件走到执行计划

用真实查询拆解联合索引的列顺序、范围条件、覆盖索引和执行计划,避免只背最左前缀原则。

联合索引的设计常被简化成一句“遵守最左前缀原则”,但真正的生产问题通常更复杂:等值条件和范围条件谁在前,排序能否利用索引,索引列是不是越多越好,以及执行计划显示用了索引为什么仍然很慢。

从查询开始,而不是从字段开始

假设订单表的核心查询如下:

1
2
3
4
5
6
7
SELECT id, user_id, status, created_at, amount
FROM orders
WHERE user_id = ?
AND status = ?
AND created_at >= ?
ORDER BY created_at DESC
LIMIT 20;

一个常见索引是:

1
2
CREATE INDEX idx_orders_user_status_created
ON orders (user_id, status, created_at DESC);

user_idstatus 是等值条件,created_at 同时承担范围过滤和排序。这个顺序使存储引擎先定位某个用户和状态下的连续索引区间,再按时间读取所需记录。

列顺序不能只看区分度

“区分度最高的列放最前面”只是经验,不是定律。索引要服务具体查询。如果系统总是先按租户隔离数据,tenant_id 即使区分度不高,也通常应处于索引前部,否则无法稳定限制扫描范围,也不利于权限边界表达。

设计时依次考虑:查询是否存在固定前缀、哪些列使用等值匹配、何处出现范围条件、是否需要支持排序与分组、索引能否被多条高频查询复用。

范围条件之后发生了什么

对于 (user_id, status, created_at),前两列等值匹配后,MySQL 可以利用第三列定位时间范围。但如果索引后面还有 amount,查询条件 amount > 100 通常不能继续缩小用于定位的索引区间。

这不代表后续列完全无用。启用索引条件下推时,存储引擎可能在索引层过滤部分记录,减少回表。因此应区分“用于索引定位”和“用于索引层过滤”,不要把 key 非空简单理解成整条条件都被高效利用。

覆盖索引的收益与成本

如果查询只返回 idstatuscreated_at,而这些值都能从二级索引获得,就可能避免回表。覆盖索引对高频列表查询很有价值,但不应把所有返回列都塞进索引。

宽索引会增加磁盘占用、降低缓存命中率,并放大插入和更新成本。长度较大的文本列、频繁变化的列尤其不适合为了覆盖查询而随意加入。生产设计需要在读收益和写成本之间取舍。

用 EXPLAIN ANALYZE 验证

MySQL 8 可以直接观察计划估算与真实执行数据:

1
2
3
4
5
6
7
8
EXPLAIN ANALYZE
SELECT id, user_id, status, created_at, amount
FROM orders
WHERE user_id = 10001
AND status = 'PAID'
AND created_at >= '2026-07-01 00:00:00'
ORDER BY created_at DESC
LIMIT 20;

重点关注实际读取行数、过滤后行数、循环次数、是否出现额外排序,以及估算与实际值是否相差过大。若估算严重失真,应检查统计信息和数据分布,而不是立刻强制指定索引。

Using filesort 也不必一概视为故障。对很小的结果集,排序成本可能低于维护一个额外索引。优化目标是降低真实查询成本,而不是消灭执行计划中的某个词。

分页越往后为什么越慢

即使索引正确,下面的深分页仍需要跳过大量记录:

1
2
3
4
5
SELECT id, created_at
FROM orders
WHERE user_id = ?
ORDER BY created_at DESC, id DESC
LIMIT 100000, 20;

更稳定的方式是游标分页:

1
2
3
4
5
6
SELECT id, created_at
FROM orders
WHERE user_id = ?
AND (created_at, id) < (?, ?)
ORDER BY created_at DESC, id DESC
LIMIT 20;

对应索引可设计为 (user_id, created_at DESC, id DESC)。游标分页避免扫描并丢弃前十万条记录,也能在数据持续写入时提供更稳定的翻页结果。

索引治理不能忽略写入

每个二级索引都会参与写入。重复索引、前缀被完全覆盖的索引和长期未使用索引,会持续消耗存储与写性能。删除前应结合慢查询、索引使用统计、业务周期和回滚方案验证,不能仅凭短时间观察下结论。

总结

联合索引没有脱离查询的“标准答案”。从高频 SQL 出发,明确等值、范围、排序和返回列,再用 EXPLAIN ANALYZE 验证真实扫描量。最左前缀是理解 B+Tree 使用方式的起点,不是索引设计的终点。