索引过多会显著拖慢INSERT/UPDATE性能、增加优化器选路开销、占用大量磁盘与内存,并提升DDL风险;删除索引前必须验证使用率、慢日志和压测,组合索引应遵循最左前缀原则精简。

INSERT/UPDATE变慢不是错觉,是索引在同步更新
每条 INSERT 或 UPDATE 执行时,MySQL 不只写主数据,还要在每个相关索引的 B+ 树里各做一次插入或删除——哪怕只是改一个带索引的字段。5 个索引 = 至少 5 次额外 I/O + CPU 计算,不是“多算一点”,是“多跑五套完整路径”。
- 常见现象:
SHOW PROCESSLIST里大量线程卡在update或insert状态;innodb_row_lock_waits指标突增;单条插入从 0.2ms 跳到 2ms+; - 唯一索引更重:每次插入前还得全索引扫描校验重复,开销远高于普通索引;
- 长字段(如
VARCHAR(255))建索引会显著拉高 B+ 树高度,页分裂更频繁,进一步拖慢写入; - 低区分度字段(如
status TINYINT只有 0/1)放在组合索引最左,会导致树内节点膨胀、查询效率反降。
优化器真会“选择困难”,尤其索引超 6 个时
MySQL 优化器需要评估所有可用索引以生成执行计划。索引数量从 3 个增加到 8 个,候选路径数可能从几十跳到几百,评估耗时成倍增长。极端情况下,它可能放弃使用任何索引,直接走全表扫描——你明明建了索引,查询却更慢了。
- 典型表现:相同 SQL 在不同负载下执行计划不一致;
EXPLAIN显示type=ALL或key=NULL,但字段明明有索引; - 组合索引
(a,b,c)和单列索引(a)同时存在,就是冗余——优化器只会选前者,后者纯属白占空间、白增维护成本; - 用
information_schema.STATISTICS查SEQ_IN_INDEX和CARDINALITY,能快速识别哪些索引从未被用过或长期低效。
磁盘和内存开销比你想的更实在
每个索引都是独立的 B+ 树结构,数据量越大、字段越宽,索引体积越夸张。10GB 的表配 5 个中等复杂度索引,额外占用 20GB+ 磁盘很常见;InnoDB 缓冲池还要缓存这些索引页,内存压力直线上升。
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
- 查真实占用:
SELECT table_name, index_name, ROUND(index_length/1024/1024, 2) AS 'size_mb' FROM information_schema.TABLES WHERE table_schema = 'your_db'; - 索引/数据比超过 1.5,就该警惕——说明索引体积已超过原始数据;
-
ALTER TABLE加字段或改类型时,MySQL 会重建所有二级索引,5 个索引可能让 DDL 操作从秒级变成分钟级,业务无法接受停机时尤其致命。
删索引比建索引风险更高,必须先验证
很多人觉得“多建几个索引总没错”,但删索引才是真正的临界操作。一旦误删高频查询依赖的索引,线上 QPS 可能瞬间腰斩。没有监控、没看慢日志、没跑过 EXPLAIN 就动手,等于闭眼拆保险丝。
- 删之前必做三件事:用
performance_schema.table_io_waits_summary_by_index_usage查近 7 天索引实际使用率;翻慢查询日志确认无 SQL 依赖该索引;在备库上先模拟删除并压测关键接口; - 组合索引要按最左前缀原则精简:比如已有
(user_id, status, created_at),再单独建(user_id)或(user_id, status)就是冗余; - 真正难的不是“建多少”,而是“留哪些”——优先保写入吞吐高的核心表的高频查询索引,其他一律砍,宁缺毋滥。
实际运维中,最常被忽略的是索引和业务节奏的匹配度:大促前加索引要预留重建时间,归档后不清理历史索引,字段语义变了但索引还挂着……这些细节比数字“5”或“6”更能决定数据库是否扛得住。










