索引不是越多越好,写多读少场景下应精简索引、删除零使用和冗余索引,遵循最左前缀原则,避免在高频更新字段上建索引,必要时延迟构建或批量禁用重建,核心是读写权衡。

在写多读少的业务场景中,索引不是越多越好,反而容易成为性能瓶颈——每次INSERT、UPDATE、DELETE都需同步维护索引,导致写入变慢、锁竞争加剧、甚至引发死锁。关键在于:**控制索引数量、聚焦高频查询字段、避免冗余和低效索引**。
精简索引,只保留真正被WHERE/JOIN/ORDER BY使用的列
很多表存在“历史遗留索引”,比如为某次临时报表加的组合索引,后续再未被使用;或单列索引与组合索引重复(如已有(a,b),又单独建了a索引)。这类索引纯属写入负担。
- 用
sys.dm_db_index_usage_stats(SQL Server)或pg_stat_all_indexes(PostgreSQL)查索引实际扫描次数,长期user_seeks = 0的可直接删除 - MySQL 可通过
performance_schema.table_io_waits_summary_by_index_usage定位零使用索引 - 组合索引遵循最左前缀原则,确保高频查询条件能命中,例如查询常带
WHERE status = ? AND created_at > ?,优先建(status, created_at)而非反过来
延迟或异步构建非核心查询索引
对报表类、后台分析类等低时效性查询,不必强求实时索引支持。可将这部分索引从主库剥离:
- 在从库或数仓中单独建索引,主库只保基础主键+1~2个核心查询索引
- 用物化视图(PostgreSQL)、汇总表(MySQL)或定时ETL预聚合替代复杂索引
- 若必须实时,考虑用覆盖索引减少回表,但注意它会增大写开销——仅当读频显著高于写频时才值得
避开更新热点字段建索引
频繁更新的字段(如updated_at、status、计数器类字段)一旦出现在索引键中,每次修改都会触发B+树节点分裂和重平衡,显著拖慢写入。
- 避免以
updated_at为前导列建索引,除非查询明确需要按更新时间范围高效过滤 - 状态字段如
status IN ('pending','processing')若值分布极不均匀(95%是'completed'),索引选择性差,宜删掉或改用部分索引(PostgreSQL)或表达式索引 - 计数类字段(如
retry_count)几乎不建议单独建索引,更不应放入组合索引前置位
批量写入时临时禁用/重建索引(谨慎使用)
针对周期性大批量导入(如日终对账、日志归档),可在业务低峰期采用“先删索引→批量插入→重建索引”策略,比逐行维护快数倍。
- SQL Server 支持
DISABLE INDEX+REBUILD;MySQL 8.0+ 可用ALTER TABLE ... DISABLE KEYS(仅MyISAM有效),InnoDB 建议用LOAD DATA INFILE配合关闭唯一检查 - 务必在事务外操作,并确保应用层无并发读写冲突
- 该法仅适用于离线或准实时场景,线上服务严禁随意禁用主键或唯一索引
不复杂但容易忽略:索引优化的本质是权衡——用少量精准索引扛住关键读请求,把其余读压力转移到应用层缓存、异步计算或查询重构上,而不是在数据库里堆索引硬扛写多读少的负载。










