频繁更新字段能否建索引取决于更新频次、查询依赖度及替代方案:单表每秒update超50次且字段在set中即属高危;需explain验证其是否为关键过滤条件,读写比>10:1才具建索引价值;优先用生成列、稳定字段联合索引或冗余表替代直接索引。

频繁更新的字段能不能作为索引,不能靠经验猜,得看三个硬指标:它被改得多不多、查得多不多、有没有更好的替代字段。核心不是“能不能加”,而是“加了之后,读的收益是不是远大于写的代价”。
先看字段更新频率是否真算“频繁”
单表每秒 UPDATE 超过 50 次,且该字段出现在 SET 子句中(比如 UPDATE order SET status = 'done' WHERE id = 123),就属于高危更新字段。更准的做法是查 performance_schema.events_statements_summary_by_digest,过滤出含该字段的 UPDATE 语句,看平均执行频次和延迟毛刺分布。如果它每秒被改几十次,还同时在多个索引里(比如单列索引 + 联合索引里都含 status),那每次更新都要触发多次 B+ 树重写,锁等待和页分裂风险会明显上升。
再看它在查询中是否真起关键过滤作用
光在 WHERE 里出现还不够,得确认它是否承担了主要筛选压力:
- 用 EXPLAIN 查真实查询:如果去掉这个索引,执行计划变成 type = all(全表扫描),加上后变成 type = ref 或 range,说明它确实扛住了查询负载;
- 检查慢查询日志:有没有大量 SELECT ... WHERE status = 'xxx' 类语句,且响应时间集中在 100ms 以上?如果有,而其他条件(如 created_at 范围)又无法单独高效过滤,那 status 就可能是唯一靠谱的入口;
- 读写比要拉出来算:如果该字段每秒被 UPDATE 20 次,但对应 SELECT 却有 300 次以上,读写比 >10:1,才初步具备建索引的价值基础。
最后评估有没有更优的替代方案
哪怕字段本身高频更新,也不一定要直接给它建索引:
- 拆解变动逻辑:比如 updated_at 总在变,但业务真正关心的是“今天更新的”,可建生成列 updated_day DATE AS (DATE(updated_at)) STORED,再对它建索引——稳定、体积小、更新开销低;
- 换字段做主过滤:订单表里,如果 created_at 和 user_id 都稳定,且能覆盖 80% 的查询场景(如按用户查近一周订单),就优先用它们建联合索引,把 status 放后面或干脆不放;
- 用冗余表分流:把 status 查询压力移到轻量聚合表(如 order_status_summary),应用双写维护,原表索引就能精简掉。
实操验证比理论判断更可靠
别只看 SHOW INDEX,要动手测:
- 临时删掉疑似问题索引(如 DROP INDEX idx_status ON t_order);
- 用生产流量压测 5 分钟,重点观察 sys.schema_table_statistics 中的 updates 延迟是否下降 ≥30%,innodb_row_lock_waits 是否明显回落;
- 如果写入性能回升显著,而关键查询没变慢(说明实际没走这个索引),那就果断移除;反之若查询变慢且 EXPLAIN 显示走了全表扫描,再考虑保留并搭配持久化统计(innodb_stats_persistent = ON)来稳住优化器判断。











