索引列顺序应按等值查询优先、高基数字段在前、范围查询置后原则排列;低基数字段单独建索引易被优化器弃用;覆盖索引需包含select所有字段但忌冗余;key_len小不等于未生效,需结合extra判断。

索引列顺序怎么排才不白建
索引列顺序不是按「常用」或「写sql时出现的顺序」来排的,而是严格按查询条件的匹配模式和字段基数(Cardinality)协同决定的。mysql 的 B+ 树索引从左到右匹配,一旦遇到范围查询(>、、<code>BETWEEN、LIKE 前缀不固定等),右侧所有列就失效了。
实操建议:
- 把等值查询(
=、IN)字段放最左边,且优先放Cardinality高的列(比如user_id比status更适合作首列) - 范围查询字段只能放最后,且最多一个——多个范围会让整个索引“断在第一个”
- 如果经常查
WHERE status = ? AND created_at > ?,别建(status, created_at),而要建(created_at, status):因为created_at是范围,放前面会废掉status;但实际中更常见的是status等值 +created_at范围,这时必须把status放前,created_at放后,否则连等值都用不上索引 - 用
SHOW INDEX FROM table_name查Cardinality值,注意它只是采样估算,不一定实时准确;低基数字段(如is_deleted TINYINT只有 0/1)单独建索引意义极小
Cardinality 不高,为什么加了索引还是走全表扫描
MySQL 优化器会对比「走索引的代价」和「全表扫描的代价」,而低 Cardinality 字段(比如 gender、is_active)导致索引区分度差,回表成本可能比直接扫表还高,优化器就干脆弃用。
常见错误现象:
- 明明建了
INDEX (is_deleted),EXPLAIN显示type: ALL -
SELECT * FROM orders WHERE is_deleted = 0占表 95%,MySQL 认为扫索引再回表不如直接扫聚簇索引
实操建议:
- 别单独给低基数字段建单列索引,除非配合
WHERE + ORDER BY或作为联合索引的后缀列 - 想强制走索引?加
FORCE INDEX是临时手段,但掩盖了设计问题;更该检查是否漏了更关键的过滤条件(比如没加user_id = ?就只查status) - 用
ANALYZE TABLE table_name更新统计信息,有时Cardinality滞后会导致误判
联合索引里要不要包含 SELECT 的字段
不需要刻意把 SELECT 列塞进索引,除非你明确需要「覆盖索引」来避免回表。MySQL 的二级索引本身只存索引列 + 主键,所以只有当 SELECT 的所有字段都在索引定义里,才能跳过聚簇索引查找。
使用场景:
- 高频查询只查几个固定字段(如
SELECT id, name, email FROM users WHERE status = ? ORDER BY created_at DESC),可建(status, created_at, id, name, email)—— 注意顺序:等值、范围、SELECT 字段 - 但加太多字段会让索引体积暴涨,写入变慢,缓存压力上升;尤其大字段(
TEXT、json)绝对不能放进索引 -
Cardinality对覆盖索引没直接影响,但索引宽度影响 B+ 树层级,间接影响性能
EXPLAIN 里 key_len 异常小,是不是索引没生效
key_len 表示 MySQL 实际用到的索引字节数,不是定义长度。它小≠没生效,而是可能只用了索引前缀,或字段本身允许 NULL / 有字符集开销。
典型原因:
-
VARCHAR(255)字段建索引但只用前 191 字节(如 utf8mb4 下),key_len显示 767 而不是 1020 - 字段定义为
NOT NULL,key_len少 1 字节(NULL 标志位) - 联合索引
(a, b, c),查询只用WHERE a = ?,key_len只算a的长度 - 如果
key_len是 0,那才是真没走索引;如果非 0 但比预期小,先看Extra有没有using index(覆盖索引)或Using where(索引下推)
查证方法:对比 SHOW CREATE TABLE 里的字段定义和字符集,再用 SELECT Length('测试') * 4 估算 utf8mb4 下的字节占用。
基数低、顺序错、字段冗余、key_len 误解——这几个点卡住的人最多,调的时候别只盯着 SQL 写法,先看 SHOW INDEX 和 EXPLAIN 输出的真实信号。