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

4次阅读

复合索引字段顺序不能随便调换,因mysql b+树按定义顺序逐列比较,必须从左到右连续匹配最左前缀才能生效;等值查询字段应前置,范围查询靠后且尽量唯一,排序字段需与索引顺序一致以避免filesort。

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

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

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' —— 跳过 statuscreated_at 不会走索引
  • WHERE user_id = 123 ORDER BY created_at —— 中间缺 status,无法保证 created_at 有序

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

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

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

如果 EXPLAIN 显示 key_len 比预期小,或 typeindex / 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) 用于排序)

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

text=ZqhQzanResources