mysql中使用复合索引提高查询效率

来源:这里教程网 时间:2026-02-28 20:48:48 作者:

复合索引的字段顺序为什么不能随便调换

MySQL 的 B+ 树索引是按定义顺序逐列比较的,

WHERE
条件只有从左到右连续匹配索引字段时才能生效。比如建了
(a, b, c)
复合索引,
WHERE a = 1 AND b = 2
能用上,但
WHERE b = 2 AND c = 3
完全无法利用该索引。

常见错误是把高频过滤字段放在后面,例如用户表按

(status, created_at)
建索引,但实际查询多为
WHERE created_at > '2024-01-01'
,此时索引几乎无效。

最左前缀原则不是“包含即可”,而是“从第一个字段开始连续命中” 等值查询字段(
=
IN
)应放前面,范围查询(
>
,
BETWEEN
,
LIKE 'abc%'
)尽量靠后且只保留一个
排序字段如果参与
ORDER BY
,需和索引顺序一致才可能避免
filesort

哪些查询能真正用上
(user_id, status, created_at)
索引

假设你建了这个三列复合索引,以下查询能命中不同长度的前缀:

SELECT * FROM orders WHERE user_id = 123;                          -- 用上 (user_id)
SELECT * FROM orders WHERE user_id = 123 AND status = 'paid';       -- 用上 (user_id, status)
SELECT * FROM orders WHERE user_id = 123 AND status = 'paid' AND created_at > '2024-01-01';  -- 用上全部三列,但 created_at 只用于过滤,不支持后续范围排序
SELECT * FROM orders WHERE user_id = 123 ORDER BY status, created_at;  -- 可跳过 filesort

但这些不行:

WHERE status = 'paid'
—— 缺少
user_id
,索引失效
WHERE user_id = 123 AND created_at > '2024-01-01'
—— 跳过
status
created_at
不会走索引
WHERE user_id = 123 ORDER BY created_at
—— 中间缺
status
,无法保证
created_at
有序

EXPLAIN 中看到
key_len
是多少才说明用对了

key_len
表示 MySQL 实际使用索引字节数。它能帮你确认是否用到了预期字段及长度。比如
user_id
BIGINT
(8 字节),
status
ENUM('paid','pending')
(1 字节),
created_at
DATETIME
(5 字节),那么:

key_len = 8
→ 只用了
user_id
key_len = 9
→ 用了
user_id + status
key_len = 14
→ 三个字段都参与了索引查找(注意:范围查询字段之后的字段仍计入
key_len
,但不再用于查找)

如果

EXPLAIN
显示
key_len
比预期小,或
type
index
/
ALL
,大概率是条件没对齐最左前缀。

什么时候该删掉冗余的复合索引

已有

(a, b, c)
,再建
(a, b)
就是冗余的——前者完全可以覆盖后者。但
(a, c)
不冗余,因为跳过了
b
,无法被
(a, b, c)
替代。

检查冗余索引可用

pt-duplicate-key-checker
,或手动比对:

新索引字段是旧索引的**严格前缀**(如旧:
(a,b,c)
,新:
(a,b)
)→ 可删新
新索引字段数更少,但顺序不同(如旧:
(a,b,c)
,新:
(a,c)
)→ 不能删,
c
在旧索引里无法单独跳过
b
使用
WHERE + ORDER BY
组合需求时,即使字段重复,也可能需要额外索引(如
(a, b)
用于查询,
(a, c)
用于排序)

复合索引不是越多越好,每个额外索引都会拖慢写入、增加磁盘占用,并让优化器选择成本上升。线上表加索引前,先看慢查日志里真实出现的

WHERE
ORDER BY
组合。

相关推荐