mysql如何选择索引列顺序_mysql基数(Cardinality)应用

2次阅读

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

mysql如何选择索引列顺序_mysql基数(Cardinality)应用

索引列顺序怎么排才不白建

索引列顺序不是按「常用」或「写sql时出现的顺序」来排的,而是严格按查询条件的匹配模式和字段基数(Cardinality)协同决定的。mysql 的 B+ 树索引从左到右匹配,一旦遇到范围查询(>、<code>BETWEENLIKE 前缀不固定等),右侧所有列就失效了。

实操建议:

  • 把等值查询(=IN)字段放最左边,且优先放 Cardinality 高的列(比如 user_idstatus 更适合作首列)
  • 范围查询字段只能放最后,且最多一个——多个范围会让整个索引“断在第一个”
  • 如果经常查 WHERE status = ? AND created_at > ?,别建 (status, created_at),而要建 (created_at, status):因为 created_at 是范围,放前面会废掉 status;但实际中更常见的是 status 等值 + created_at 范围,这时必须把 status 放前,created_at 放后,否则连等值都用不上索引
  • SHOW INDEX FROM table_nameCardinality 值,注意它只是采样估算,不一定实时准确;低基数字段(如 is_deleted TINYINT 只有 0/1)单独建索引意义极小

Cardinality 不高,为什么加了索引还是走全表扫描

MySQL 优化器会对比「走索引的代价」和「全表扫描的代价」,而低 Cardinality 字段(比如 genderis_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 字段
  • 但加太多字段会让索引体积暴涨,写入变慢,缓存压力上升;尤其大字段(TEXTjson)绝对不能放进索引
  • Cardinality 对覆盖索引没直接影响,但索引宽度影响 B+ 树层级,间接影响性能

EXPLAIN 里 key_len 异常小,是不是索引没生效

key_len 表示 MySQL 实际用到的索引字节数,不是定义长度。它小≠没生效,而是可能只用了索引前缀,或字段本身允许 NULL / 有字符集开销。

典型原因:

  • VARCHAR(255) 字段建索引但只用前 191 字节(如 utf8mb4 下),key_len 显示 767 而不是 1020
  • 字段定义为 NOT NULLkey_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 INDEXEXPLAIN 输出的真实信号。

text=ZqhQzanResources