mysql索引依赖表结构设计,字段类型、长度、null约束等直接影响索引效率;联合索引需按查询条件顺序构建,避免函数操作与隐式转换;大字段和频繁更新列会显著增加索引维护成本。

索引是表结构的延伸,不是事后补丁
MySQL 的索引不能脱离表结构独立存在——PRIMARY KEY、UNIQUE、INDEX 都绑定在具体列上,而这些列的类型、是否允许 NULL、字符集、排序规则(COLLATION),直接决定索引能否生效、是否紧凑、是否支持范围查询。比如把 user_id 设为 VARCHAR(255) 却只存 8 位数字,不仅浪费空间,还会让 B+Tree 索引页更稀疏,查询变慢;又比如在 TEXT 列上建普通索引会报错,必须加前缀长度(INDEX(content(100))),但前缀太短可能失效,太长又拖慢写入。
联合索引顺序必须匹配高频查询的 WHERE 条件顺序
联合索引 (a, b, c) 可以加速 WHERE a = ?、WHERE a = ? AND b = ?、WHERE a = ? AND b = ? AND c = ?,但对 WHERE b = ? 或 WHERE b = ? AND c = ? 完全无效。这是因为 B+Tree 是按最左列排序构建的。实际设计时要盯住慢查询日志里的 WHERE 和 ORDER BY 子句:
- 把等值查询(
=、IN)列放最左 - 范围查询(
>、BETWEEN、LIKE 'abc%')只能放最后一位,且后面列不再生效 -
ORDER BY字段如果和查询条件列重叠,可合并进联合索引减少排序开销
EXPLAIN 显示 type=ALL 或 rows 过大,大概率是表结构没为索引留出空间
常见诱因不是“没建索引”,而是表结构本身阻碍了索引使用:
- 对索引列做函数操作:
WHERE YEAR(create_time) = 2024→ 改成WHERE create_time >= '2024-01-01' AND create_time - 隐式类型转换:
user_id是INT,却传字符串'123'→ MySQL 自动转类型,导致索引失效 -
CHAR列存短字符串但定义过长(如CHAR(64)存 5 位编码),比较时补空格,破坏索引有序性;改用VARCHAR更稳妥 - 未设置
NOT NULL的列,IS NULL查询无法走索引(除非单独建IS NULL索引,但代价高)
大字段(TEXT/BLOB)、频繁更新列、JSON 类型会显著拖慢索引维护
MySQL 的二级索引叶子节点存的是主键值,不是整行数据,所以理论上不影响存储体积。但现实里有三个隐藏成本:
- 插入/更新带
TEXT的行时,InnoDB 要先写入行数据到溢出页(off-page),再更新聚簇索引和所有二级索引——IO 压力翻倍 -
JSON字段即使不建索引,也会在INSERT时做解析校验;若建了虚拟列索引(GENERATED COLUMN + INDEX),每次更新 JSON 都要重新计算虚拟列值 - 频繁更新的列(如
view_count)出现在联合索引中,会导致对应索引页频繁分裂,产生碎片;这类列更适合单独建索引或不做索引,靠缓存扛读
真正难处理的,往往不是“要不要加索引”,而是“这张表的字段定义方式,已经让索引很难高效工作”。优化得从 CREATE TABLE 语句第一行开始看起。










