等值条件字段必须置于联合索引最左侧,因B+树按序逐层过滤,范围查询或缺失条件会使后续字段失效;多等值时按查询频率而非区分度排序;ORDER BY/GROUP BY字段须紧接等值字段后;需避免隐式类型转换导致索引失效。

等值条件字段必须放联合索引最左边
MySQL 的 B+ 树索引是按字段顺序逐层过滤的,一旦遇到范围查询(>、BETWEEN、LIKE 'abc%')或缺失条件,后续字段就无法走索引。比如 WHERE status = 'paid' AND created_at > '2024-01-01' AND user_id = 123,如果索引是 (status, created_at, user_id),只有前两个字段能用上;但把 user_id 换到最左,它根本不会被用到——因为 user_id = 123 是等值,但它不在最左,优化器跳过它。
实操建议:
- 所有等值条件(
=、IN、IS NULL)字段,优先塞进索引最左侧连续位置 - 多个等值条件时,按查询频率从高到低排,不是按区分度排
- 用
EXPLAIN看key_len:值越接近索引总字节数,说明用得越全
高区分度字段前置只在无等值条件时才关键
区分度(cardinality)影响的是索引扫描行数,但前提是它能被用上。如果一个字段从不单独出现在 WHERE 中,或者总被写在范围条件之后,再高的区分度也白搭。比如日志表里 request_id 区分度接近 100%,但查询总是 WHERE app = 'web' AND ts > NOW() - INTERVAL 1 HOUR,那把 request_id 放索引第一位毫无意义。
实操建议:
- 先确认该字段是否常作为等值条件独立出现;否则别迷信“高区分度优先”
- 用
SELECT COUNT(DISTINCT col) / COUNT(*) FROM tbl粗算区分度,低于 0.01 就算低区分度(如status只有 3 个值) - 对低区分度字段(如
is_deleted),除非配合强制索引或覆盖查询,否则别放联合索引靠前位置
ORDER BY 和 GROUP BY 字段要接在等值条件后面
如果查询带 ORDER BY a, b,且 a、b 都没在 WHERE 中做等值过滤,那索引 (a, b) 能避免排序;但如果 WHERE c = 1,又想按 a 排序,索引就得是 (c, a, b)——c 保证索引范围,a 和 b 才能天然有序。
实操建议:
-
ORDER BY字段必须紧跟等值字段之后,中间不能插范围字段 -
GROUP BY同理,且注意 MySQL 8.0+ 对松散索引扫描(Loose Index Scan)要求更严格:分组字段必须连续、等值前置、无函数包装 - 避免
ORDER BY中混用 ASC/DESC,MySQL 仅在 8.0+ 支持混合方向索引,老版本会直接退化为 filesort
别忽略隐式类型转换导致的索引失效
字段是 VARCHAR,但查询写成 WHERE user_id = 123(数字),MySQL 会把每行字符串转成数字比对,索引失效。错误现象很典型:EXPLAIN 显示 type: ALL,key: NULL,哪怕字段上有索引。
实操建议:
- 检查
SHOW CREATE TABLE确认字段类型,WHERE 中的字面量必须匹配——VARCHAR就用'123',INT才用123 - 用
SELECT @@sql_mode确保没开STRICT_TRANS_TABLES外的宽松模式,否则隐式转换更隐蔽 - 对 JSON 字段或生成列,更要小心函数包裹:
WHERE JSON_EXTRACT(data, '$.id') = '123'绝对不走索引,得建函数索引或冗余普通字段
字段顺序不是纯理论排序题,它和你实际怎么写 WHERE、有没有 ORDER、数据类型是否对齐,全绑在一起。少一个条件对齐,前面全白调。










