索引优化必须结合表结构设计,忽略字段类型、null约束、字符集等会导致索引失效;explain中type=all或key=null常因定义与索引不匹配;前缀索引、联合索引顺序、null处理、排序规则、大字段限制及统计信息更新均需精准适配。

索引优化必须基于表结构设计来展开
单独建索引却忽略字段类型、长度、是否允许 NULL、主键选择等,大概率会失效或拖慢写入。MySQL 的 EXPLAIN 显示 type=ALL 或 key=NULL,往往不是索引没建,而是字段定义和索引不匹配。
-
VARCHAR(255)上建前缀索引要小心:如果业务常查前 10 字符,INDEX(col(10))可行;但若WHERE col = 'xxx'是完整值匹配,而索引只截了前 5 位,就无法用上 - 联合索引顺序必须贴合查询条件顺序:
INDEX(a, b, c)能加速WHERE a=1 AND b=2,但对WHERE b=2 AND c=3无效 -
TINYINT和ENUM做状态字段时,如果经常ORDER BY status DESC,记得在索引里包含该字段并显式声明DESC(MySQL 8.0+ 支持)
NULL 值处理直接影响索引可用性
MySQL 中,含 NULL 的列在 B+ 树索引中会被特殊存储,导致部分场景下无法使用索引做范围扫描或排序。比如 status TINYINT NULL,即使加了 INDEX(status),WHERE status != 1 也可能走全表扫描——因为 NULL != 1 恒为 true,优化器不敢依赖索引过滤。
- 能用
NOT NULL就别留空:除非业务语义明确需要“未知”,否则默认设NOT NULL DEFAULT 0 - 查询中避免对可空字段用
!=或NOT IN,改用IS NOT NULL AND status != 1显式排除 - 联合索引里尽量把可能为
NULL的字段放后面,否则前导列一旦为NULL,整条索引路径就断了
字符集与排序规则影响索引比较逻辑
同一个 VARCHAR 字段,在 utf8mb4_general_ci 和 utf8mb4_0900_as_cs 下,索引的匹配行为完全不同。大小写敏感(_cs)或重音敏感(_as)会导致隐式转换,让索引失效。
- 查拼音首字母时,
WHERE name LIKE 'Z%'在_ci排序下能走索引;但在_cs下若数据存的是小写zhang,就匹配不上 - 联表字段字符集不一致(如 A 表用
utf8mb4_unicode_ci,B 表用utf8mb4_general_ci),即使都有索引,JOIN也可能放弃使用索引,转为 Block Nested-Loop - 新建表统一用
utf8mb4_0900_as_cs(MySQL 8.0+),避免旧排序规则的模糊匹配陷阱
大字段(TEXT/BLOB)和虚拟列对索引的限制
MySQL 不允许直接对 TEXT 或 BLOB 列建普通索引,也不支持在它们上面建函数索引(如 JSON_EXTRACT() 结果)。但可通过生成列(generated column)绕过。
- 想对 JSON 字段里的
$.user_id建索引?先加生成列:user_id INT AS (JSON_UNQUOTE(JSON_EXTRACT(data, '$.user_id'))) STORED,再对user_id建索引 -
TEXT字段需前缀索引时,长度不能超 3072 字节(InnoDB 页面限制),且前缀长度必须是字符数,不是字节数——utf8mb4下一个汉字占 4 字节,INDEX(content(100))实际最多覆盖 100 个字符,不是 100 字节 - 虚拟列(
STORED)会占用磁盘空间,但能被索引和EXPLAIN正确识别;VIRTUAL列不存盘,但多数版本不支持对其建索引
ANALYZE TABLE 更新统计信息。InnoDB 的索引选择严重依赖这些数据,旧统计可能导致优化器坚持走错路。










