索引越多,INSERT/UPDATE/DELETE越慢,因每新增记录需同步更新所有相关索引的B+树,引发插入定位、页分裂、指针调整及磁盘刷盘等开销,高并发下易成瓶颈。
索引越多,INSERT/UPDATE/DELETE 越慢
每新增一条记录,MySQL 不只是写数据行,还要同步更新所有相关索引的 B+ 树结构。每个索引都是一份独立的、有序的数据副本,写入时都要做插入定位、页分裂、指针调整甚至磁盘刷盘。尤其在高并发写入场景下,索引维护开销会直接变成瓶颈。
常见错误现象:
延迟突然升高;
里大量线程卡在
或
状态;
指标明显上升。
每多一个二级索引,写操作至少多一次随机 I/O(尤其是非缓存命中时)
唯一索引还要额外校验重复值,比普通索引更重
/
类字段建索引,若实际值很长,索引页膨胀快,分裂更频繁
联合索引顺序不合理(比如把低区分度字段放前面),会导致索引树深度增加,写入路径变长
如何判断哪些索引是“冗余”或“无效”的
不是所有索引都在被用。很多是历史遗留、开发随手加、或只在某次慢查里临时加完就忘了删。真正有效的索引,应该有稳定且可衡量的查询收益,否则就是纯成本。
使用场景:线上表已运行数月,但没人定期看索引使用情况。
查
(MySQL 8.0+),它基于 performance_schema 统计,能直接列出长期未被任何查询使用的索引
用
对比
和实际
中出现的索引,找“从不出现”的
注意联合索引的前缀覆盖:如果已有
,再建
就是冗余;但
不一定冗余,因为最左前缀匹配规则不同
唯一索引和主键冲突?
看是否有多个
约束同一组字段
ALTER TABLE DROP INDEX 的实际影响和安全前提
删索引不是“立刻变快”,而是释放后续所有写入的开销。但操作本身会锁表(MySQL 5.6+ 的
可避免全表锁,仍需元数据锁等待),且不可逆——删错可能让某条关键查询从 10ms 慢成 10s。
MySQL(Linux)
MySQL 9.6.0是面向Linux平台的2026年创新版本,核心架构迎来重大革新。其将外键约束与级联操作从InnoDB引擎层上移至SQL层,确保所有数据变更均被完整记录至Binlog,彻底解决了CDC(变更数据捕获)与主从复制中的数据不一致难题。此外,该版本引入container_aware启动选项以原生适配容器环境,并对审计日志进行了组件化重构,为追求极致数据一致性与云原生体验的开发者提供了全新选择。
下载
参数差异:
在 MySQL 5.7 默认走
,但若索引包含全文或空间类型,则强制
;MySQL 8.0 默认更激进,但仍建议显式加
并观察执行时间。
务必先在从库或影子库上跑一遍,确认没业务报错(比如应用层依赖
异常做逻辑分支)
避开高峰期,
可绕过锁表,但会延长操作时间,且对大表容易拖垮复制延迟
删完立刻查
验证,别只信命令返回的
监控删索引后
和
是否同步下降——说明查询确实不再依赖该索引了
单列索引 vs 联合索引:选哪个更省写入成本
三个单列索引
、
、
和一个联合索引
,写入开销差得远。前者要维护三棵 B+ 树,后者只一棵,但查询能力不一定弱——关键看 WHERE 条件怎么组合。
性能影响:联合索引写入压力≈单个单列索引,但能覆盖更多查询模式;而三个单列索引会让每次写入触发三次独立的树维护流程。
优先按高频查询条件排序建联合索引,例如经常查
,就建
,别拆成两个单列
区分度高的字段放前面(如
),低区分度的放后面(如
),能减少树的无效分支
不要为了“看起来全面”建
—— 超过 4 列的联合索引,写入放大明显,且很少有查询真能用满全部列
注意
和
字段是否能被同一联合索引覆盖,否则排序/分组会额外触发 filesort
索引成本藏在每次写入的毫秒级延迟里,积少成多才压垮系统。最危险的不是没索引,而是有一堆没人敢动、也没人记得为什么存在的索引。
INSERTSHOW PROCESSLISTupdateinsertinnodb_row_lock_waitsTEXTVARCHAR(255)sys.schema_unused_indexesSELECT * FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = 'db' AND TABLE_NAME = 't';INDEX_NAMEEXPLAIN(a,b,c)(a,b)(b,a)SHOW CREATE TABLE tUNIQUE KEYALGORITHM=INPLACEDROP INDEX idx_name ON tINPLACECOPYALGORITHM=INPLACE, LOCK=NONEDUPLICATE KEYpt-online-schema-changeinformation_schema.STATISTICSQuery OKInnodb_buffer_pool_read_requestsInnodb_buffer_pool_reads(a)(b)(c)(a,b,c)WHERE a=1 AND b>10(a,b)user_idstatus(a,b,c,d,e)ORDER BYGROUP BY