索引并非越多越好,过多索引会显著拖慢写操作、扩大锁范围、增加优化器决策开销并导致误选,低区分度、小表、text前缀等场景建索引反而降低性能。

写操作变慢不是错觉,是索引在同步更新
每次 INSERT、UPDATE 或 DELETE,MySQL 都得把对应行在所有相关索引里“重写一遍”。你加了 5 个索引,它就得多跑 5 次 B+ 树插入/分裂/合并——这不是多算一次,是多做五套完整路径。
- 高频写入表(比如日志表
operation_logs)加了INDEX idx_user_id和INDEX idx_created_at,每秒 1000 条写入可能直接卡在索引维护上 - 事务中批量更新时,锁范围可能因索引增多而扩大,出现更长的锁等待甚至死锁
-
ALTER TABLE ... ADD INDEX不仅耗时,还会阻塞后续 DML,大表上线前务必在低峰期执行
优化器真会“选花眼”,不是所有索引都能被用上
MySQL 查询优化器要从所有可用索引里挑一个“看起来最快”的。索引一多,它不光要评估成本,还要读统计信息、预估行数、比对索引深度……这个过程本身就有开销,而且容易误判。
- 已有联合索引
(user_id, status),再单独建INDEX idx_user_id,后者大概率被忽略——优化器知道前者能覆盖单列查询 -
EXPLAIN显示type: ALL或key: NULL,说明索引根本没被选中,别急着加新索引,先看是不是查询条件没走最左前缀 - 统计信息过期(比如大表刚导入数据但没
ANALYZE TABLE)会导致优化器低估/高估索引效率,选错索引
小表、低区分度字段、TEXT 类型,建索引等于白忙
索引加速的前提是:它真能大幅减少扫描行数。如果扫出来一半数据,或者字段值就两三种,或者内容太长只存前缀,那索引不仅没用,还拖后腿。
- 用户表
users有 1000 万行,gender只有 'M'/'F'/'O',查WHERE gender = 'M'优化器直接全表扫描——顺序读比随机回表快得多 - 表不到 1000 行,
SELECT * FROM config WHERE k = 'timeout',全表扫描可能只要一次磁盘页读,加索引反而多一次树查找 -
TEXT列不能直接建索引,INDEX idx_content (content(255))这种前缀索引对模糊匹配(LIKE '%xxx')完全无效
怎么判断该不该加?看真实查询 + 覆盖需求
别靠感觉,拿 EXPLAIN 和慢查询日志说话。重点不是“有没有索引”,而是“当前查询是否真的受益于它”。
- 高频查询
SELECT name, email FROM users WHERE age > 25 ORDER BY created_at DESC,优先考虑覆盖索引INDEX idx_age_name_email_created (age, name, email, created_at),避免回表 - 用
pt-duplicate-key-checker扫描冗余索引,比如(a,b)和(a)共存,删掉后者 - 上线后观察
SHOW INDEX FROM table_name中的Cardinality值,接近 0 或远小于表总行数,说明该索引基本没被用于过滤
索引不是开关,开了就亮;它是杠杆,支点偏一点,省力变费力。真正难的不是建索引,是判断哪条查询值得用空间和写性能去换——而这只能靠线上数据反馈,不是靠开发时拍脑袋。











