WHERE 条件中字段的出现顺序决定索引列顺序
MySQL 的 B+ 树索引是按定义顺序逐列比较的,
WHERE a = 1 AND b = 2 AND c > 3这类查询,只有把
a放在索引最左,才能用上整个索引。如果索引是
(b, a, c),那
a就无法走最左前缀,只能用到
b那一列(前提是
b有等值条件)。
实操建议:
把等值查询(=、
IN)字段放前面,范围查询(
>、
BETWEEN、
LIKE 'abc%')放后面 如果多个字段都是等值查询,把区分度高(基数大)的字段放更左,比如
user_id比
status更适合作为第一列 避免把经常单独查的低频字段放在最左,否则其他组合查询可能用不上这个索引
ORDER BY 和 GROUP BY 字段必须紧接在 WHERE 等值条件之后
MySQL 只有在索引能“覆盖”排序或分组时,才避免额外的 filesort 或 temporary table。例如
SELECT * FROM t WHERE a = 1 ORDER BY b, c,索引
(a, b, c)可以复用;但若写成
(a, c, b),
b不在连续位置,就无法利用索引排序。
常见错误现象:
EXPLAIN结果中
Extra列出现
Using filesort,说明排序没走索引
GROUP BY后字段顺序和索引不一致,哪怕所有字段都在索引里,也可能触发临时表 混合 ASC/DESC(如
ORDER BY b ASC, c DESC)在 MySQL 8.0 之前无法用索引排序,8.0+ 要求索引定义也显式声明方向
联合索引不是“字段集合”,重复建单列索引往往没用
有联合索引
(a, b, c),它天然包含
(a)和
(a, b)的能力,但不包含
(b)或
(c)单独查询的能力。此时再单独建
INDEX(b),不是补充,而是冗余——除非你真有高频的
WHERE b = ?查询且无法改写 SQL。
性能与维护影响:
每个索引都增加写开销(INSERT/UPDATE/DELETE 都要更新索引树) 索引占用磁盘和内存,过多索引会拖慢 buffer pool 命中率 用SHOW INDEX FROM table_name查看
Cardinality,长期为 0 或极低的索引值得删掉
WHERE 中用了函数或表达式,索引直接失效
像
WHERE YEAR(create_time) = 2023或
WHERE phone LIKE '%138%'(左侧通配),MySQL 无法用索引定位,因为索引存的是原始值,不是计算结果。
可选替代方案:
把函数移到右边:create_time >= '2023-01-01' AND create_time对固定前缀模糊查,用
LIKE '138%',它能走索引;但
LIKE '%138'或
LIKE '%138%'不行 实在要函数查询,考虑生成列(generated column)+ 函数索引(MySQL 5.7+):
ALTER TABLE t ADD COLUMN y INT AS (YEAR(create_time)) STORED, ADD INDEX idx_y(y)
真正容易被忽略的是:索引优化不能只看单条 SQL,得看整体查询模式。一个
(a, b, c)索引可能让三条不同 WHERE 条件的查询受益,也可能因其中一条查
c单独出现而完全浪费。上线前用
pt-query-digest或 slow log 统计真实查询分布,比凭经验猜更可靠。
