索引越多,insert/update/delete越慢,因每次写操作需同步更新所有相关索引的b+树;unique索引、长字符串索引及频繁更新列上的索引影响最大;应基于实际查询需求取舍,避免冗余索引。

索引越多,INSERT/UPDATE/DELETE 越慢
每新增一条记录或修改带索引的字段时,MySQL 不仅要写数据页,还要同步更新所有相关索引的 B+ 树结构。这意味着:INSERT 每多一个二级索引,就要多一次随机磁盘写(或缓冲池刷脏);UPDATE 若修改了索引列,可能触发索引项删除 + 重建;DELETE 则需在每个索引中定位并移除对应条目。
常见错误现象包括:
- 批量导入 10 万行数据,加了 5 个二级索引后耗时从 2 秒涨到 47 秒
-
UPDATE user SET status = 1 WHERE id = 123很快,但改成UPDATE user SET email = 'x@y.z' WHERE id = 123后变慢——因为email是唯一索引,需校验冲突
哪些索引对写入影响最大
影响程度取决于索引类型、是否唯一、以及字段更新频率。排序从高到低:
-
UNIQUE索引:每次写入都必须做重复值校验,涉及索引查找 + 锁定范围,开销显著高于普通索引 - 长字符串字段上的索引(如
VARCHAR(500)):B+ 树节点存储更多字节,导致页分裂更频繁,写放大更严重 - 频繁更新的列上的索引(如
updated_at、status):每次UPDATE都触发索引维护 - 复合索引中靠前的列若常被更新,整条索引项都要挪动;靠后的列更新则影响小得多
如何在读写间做实际取舍
没有通用最优解,但可按以下原则快速判断:
- 先用
EXPLAIN确认查询真正在用哪个索引,避免“以为有用其实没走”的假索引 - 对写入密集型表(如日志、消息队列),优先保留
PRIMARY KEY和极少数高频WHERE条件索引,其余改用应用层缓存或异步聚合查询 - 批量写入前临时禁用非必要索引(仅限
MyISAM;InnoDB不支持,但可用DROP INDEX+ADD INDEX替代,注意锁表时间) - 用
innodb_change_buffering = all(默认)让插入/更新的二级索引变更先缓存在内存,减少随机 IO —— 但会增加缓冲池压力,且崩溃后需重放 change buffer
SHOW VARIABLES LIKE 'innodb_change_buffering';
容易被忽略的隐性成本
索引不仅拖慢写入,还会悄悄吃掉更多资源:
- 每个索引都占用
ibdata1或独立表空间,备份体积和恢复时间线性增长 -
SELECT COUNT(*)在无WHERE时,InnoDB 必须遍历主键或某个索引——如果只有大字段的二级索引,反而比全表扫还慢 - 很多 ORM 自动生成的“防错索引”(如给所有外键加索引)并无真实查询支撑,纯属冗余
-
ANALYZE TABLE会重新采样索引统计信息,索引越多,采样越久,期间可能阻塞 DML
真正需要权衡的从来不是“要不要索引”,而是“这个索引有没有被稳定、高频、不可替代地用到”。











