表上索引越加越多,批量导入从几分钟涨到十几分钟,是不是索引加多了?
索引确实可能加多了。每个二级索引都要在插入、更新、删除时同步维护,写密集表的批量导入、单条写入 P99 和主从延迟会一起变差。按 2026 年常见交付经验,读多写少的后台表单表 5~8 个索引较常见,混合型业务表 3~6 个,写密集流水表 2~4 个;但数量只是信号,重复索引、低选择性索引和可被联合索引覆盖的单列索引,才是优先清理对象。是否该删,先看它是否对应可识别的高频查询,再看写入代价能否接受。
索引为什么会让写入变慢
InnoDB 写入一行,不只是往聚簇索引里放一条记录,还要更新每一个二级索引。索引越多,一次写事务要改的 B+ 树页越多,redo、undo、buffer pool 的占用也越大。更新被索引覆盖的列时,索引维护还会额外发生。它不是简单的线性关系,但写入频率越高,索引数量的代价越明显。
在项目里常见的情况是:订单流水表、日志表、消息表刚上线时查询少,开发顺手给每个条件都建了索引,半年后批量导入从几分钟涨到十几分钟,主从延迟也从秒级拉到分钟级。此时只加机器或加连接,往往压不住,因为瓶颈在写放大和索引维护上。
- 写放大:每多一个二级索引,插入和更新都要多写一份索引页。
- 页分裂:随机写入的索引更容易触发页分裂,影响连续写入吞吐。
- 缓存占用:索引页挤占 buffer pool,热数据命中率下降。
- 锁与事务:索引维护让写事务更长,锁持有时间可能被拉长。
- DDL 成本:后续加索引、改索引也更慢,发布窗口更难安排。
怎么判断哪些索引该删:三看一验法
索引治理不能靠感觉,也不能只盯数量。我习惯用一个可落地的框架:三看一验。先看冗余、再看选择性、再看读写比,最后用执行计划和索引使用统计验证。每一步都要留下证据,避免把核心查询依赖的索引误删。
- 看冗余:两个索引前缀相同、后列被另一个联合索引覆盖,或者主键列又单独建了索引,通常属于可合并对象。
- 看选择性:性别、状态、布尔值这类低区分度列,单独建索引往往不如并入联合索引;联合索引把选择性高的列放前面更常见。
- 看读写比:写多读少的表,索引数量要更克制;读多写少的后台表,可以保留更多查询覆盖索引。
- 验使用情况:用执行计划、慢查询和索引使用统计核对,按数据库官方文档确认视图含义,观察一个完整业务周期再决定。
合格线可以这样定:删索引前有执行计划对比,删之后核心接口 P95 没有明显上升,写入耗时改善能用量化指标说明,并且保留回滚脚本和低峰窗口。做不到这几条,就不要在白天直接删生产索引。
交付现场:批量导入变慢时的索引清理
在 2026 年一次项目交付中,一张订单流水表当时有 11 个索引,其中 3 个是前缀重复的单列索引,2 个是低选择性状态索引。约束是白天不能停写、主从延迟已到分钟级,批量导入耗时约 12 分钟。做法是先在低峰窗口用执行计划核对高频查询,把重复索引合并为一个联合索引,低选择性索引只保留在联合索引末尾,并准备回滚脚本。结果批量导入耗时降到 7 分钟左右,写入 P99 下降,但代价是当晚 DDL 期间复制延迟短时升高,需要错峰观察。经验区间:写密集表保留 2~4 个索引,清理后写入改善通常以批量导入耗时和 P99 衡量。
加索引之前,先按这个顺序找替代
不是每个慢查询都要新增索引。更稳妥的顺序是先改写查询,再看能否并入现有联合索引,最后才考虑新增单列索引。很多所谓缺索引,其实是 SQL 扫了太多行、返回了太多列,或者联合索引列顺序没排好。
- 先减少扫描行数:补上等值条件、避免函数包列、避免隐式类型转换。
- 再并入联合索引:高频查询条件能否被现有联合索引的最左前缀覆盖,范围列尽量靠后。
- 再考虑覆盖索引:让查询只读索引页,减少回表;但覆盖列太多会推高索引体积。
- 再评估归档与分区:历史数据冷热分离后,索引压力可能自然下降。
- 最后才新增单列索引:新增前先确认它不会被现有索引替代,且写入代价可接受。
顺序颠倒的代价很直接:索引加了一堆,写入更慢,查询却因为列顺序或选择性不足没走新索引。此时再回头删,又要经历一轮执行计划验证和低峰发布。
不同表的索引数量经验区间
索引数量没有统一标准,但可以按表的读写特征划出经验区间。下面这组数字适合拿来复查,不适合当成硬性指标。判断一个索引该不该留,最终看它是否服务于可识别的高频查询。
- 读多写少的后台表、配置表、报表维表:单表 5~8 个索引较常见,重点是覆盖高频查询和排序。
- 混合型业务主表,如订单、用户、商品:3~6 个较常见,优先用联合索引覆盖多个查询。
- 写密集流水表,如日志、事件、消息、轨迹:2~4 个较常见,保留主键和少量关键查询索引即可。
- 小表或缓存命中很高的表:索引多一点对写入影响有限,不必为了数量强行删。
这里的关键不是记住数字,而是养成一个习惯:新增索引前问一句,它对应哪个可识别的高频查询?如果答不上来,它大概率就是未来要清理的候选。
适用场景与边界
适合做索引治理的信号包括:写入延迟持续变高、批量导入明显变慢、主从延迟扩大、单表索引数量明显超过高频查询数量、慢查询集中在写入而不是读取。这些情况下,按三看一验法清理冗余索引,通常比继续加硬件更直接。
不适合大动的情况也要说清楚:读多写少、表数据量只有几万到几十万行、核心查询本来就依赖这些索引、删除后会让查询从索引扫描退回全表扫描。边界句可以记成:如果删索引后核心查询从索引扫描变成全表扫描,即使写入改善,也通常不值得;如果表小且缓存命中高,索引多一点对写入影响有限。
常见坑与验收口径
索引治理最常见的坑,是把没统计到使用直接当成没用。索引使用统计有窗口偏差,月底报表、季度对账、后台导出可能几周才跑一次。删之前最好观察一个完整业务周期,至少覆盖一次月结或高峰场景。
- 直接删生产索引:没有执行计划对比和回滚脚本,晚高峰回退困难。
- 只看数量不看查询:读多写少的表删过头,查询性能反而下降。
- 用单列索引堆数量:能用联合索引覆盖的场景,拆成多个单列索引更浪费。
- 忽略 DDL 影响:加索引、删索引本身会占用 IO,可能推高复制延迟。
- 误判瓶颈:主从延迟高也可能是大事务、大批量导入或 DDL,不一定是索引数量。
验收口径建议写成三条:核心查询执行计划不退化、写入耗时或导入耗时改善可量化、回滚脚本在一个发布周期内可用。三条都满足,才算一次合格的索引清理。
常见问题
索引多了会让插入变慢吗?
会。写密集表每多一个二级索引,插入和更新都要多维护一份索引页,常见表现是批量导入耗时增加、写入 P99 抬高、主从延迟扩大。
怎么知道某个索引有没有被用到?
可结合执行计划、慢查询和索引使用统计判断,按数据库官方文档核对视图口径;统计有窗口偏差,建议观察一个完整业务周期再做结论。
联合索引能替代多个单列索引吗?
部分能。高频查询条件能被联合索引的最左前缀覆盖时,通常不必再建单列索引;但排序、范围和选择性会改变可用性,仍要看执行计划。
单表索引控制在几个比较合适?
没有固定值。读多写少常见 5~8 个,混合型 3~6 个,写密集流水表 2~4 个,属于经验区间,最终以是否有明确高频查询为准。
删索引要停机吗?
多数在线 DDL 可在业务低峰执行,但会占用 IO 并可能推高复制延迟。建议低峰、分批、留回滚脚本,具体可按数据库官方文档核对。
如果你在 2026 年遇到写入变慢且单表索引明显偏多,先做一轮三看一验,把候选索引、执行计划、回滚脚本一起放进发布单;但若表小、读多写少,或者删除会让核心查询走全表扫描,就不必为了数量好看去动它。在项目交付里,通常把索引清单和验证记录作为数据库变更的一部分。
-
订单列表滚到很后面就转圈,offset 越大越慢,能不能只把每页条数调小?
日期:2026年9月29日 阅读:36
-
活动一开始接口就被打满,QPS 从平时平稳冲到活动峰值,是不是只把限流配在网关就漏了?
日期:2026年9月28日 阅读:88
-
运营要一次导出十万行订单,接口老是内存爆掉,只能让他分批导吗?
日期:2026年9月26日 阅读:42
-
手机号加密后运营按号码查用户,只能全表解密再比对吗?
日期:2026年9月25日 阅读:45
-
库存和订单同时更新,偶尔报 Deadlock found,只把重试次数调大能压住吗?
日期:2026年9月24日 阅读:36




