加where条件并确保走索引最有效;性能抖动源于脏页刷盘、锁升级、执行计划漂移集中爆发;update无索引会触发临键锁致锁表;需用explain验证type为ref/range;高频字段建联合索引按等值→范围排序;避免在索引列上用函数。

直接加 WHERE 条件且确保走索引,比任何事后调优都管用。性能抖动不是“偶尔慢”,而是脏页刷盘、锁升级、执行计划漂移这些机制在特定条件下集中爆发的结果。
UPDATE 没走索引时会锁整张表
MySQL 的 UPDATE 在无索引字段上执行,会退化为临键锁(Next-Key Lock),实际效果接近锁表。哪怕只更新 1 行,WHERE status = 'pending' 若 status 无索引,就可能阻塞所有写入。
- 用
EXPLAIN确认type是ref或range,而非ALL或index - 高频查询字段优先建联合索引,顺序按「等值 → 范围」排,例如
(user_id, status, created_at) - 避免在索引列上用函数:
WHERE DATE(created_at) = '2024-01-01'会让索引失效,改用WHERE created_at >= '2024-01-01' AND created_at
脏页刷盘触发的抖动比你想的更频繁
不是只有大事务才刷脏页——当 innodb_max_dirty_pages_pct 设得太低(比如 20),InnoDB 反而会高频小批量刷页,打断正常查询节奏,尤其在写入密集时形成「刷页抢内存 → 查询卡住 → 更多脏页积压」的循环。
- SSD 场景建议设为
60~75;HDD 建议50~60 - 该参数只影响新刷页决策,已有脏页仍按旧策略处理,不能靠
SET GLOBAL立即见效 - 配合
innodb_io_capacity观察:SSD 可设为 2000+,HDD 控制在 200 左右
执行计划抖动让 UPDATE 行为不可预测
同一句 UPDATE 在不同时间可能生成完全不同执行路径:统计信息过期、绑定变量窥探偏差、隐式类型转换都会导致优化器误判扫描行数,进而影响锁范围和刷脏页量。
- 检查
dba_tab_statistics.last_analyzed(Oracle)或information_schema.STATISTICS(MySQL),对高频 DML 表定期ANALYZE TABLE - 避免
WHERE中混用字符串和数字:WHERE user_id = '123'可能触发全表扫描,应统一为WHERE user_id = 123 - 若使用绑定变量且参数值分布极不均匀(如 95% 是
'ACTIVE',5% 是'CANCELED'),考虑拆成两条确定性 SQL 或启用自适应游标共享(ACS)
真正容易被忽略的是:抖动往往不是单点问题,而是多个机制叠加——比如一个没索引的 UPDATE 先锁住大量行,导致后续查询被迫加载更多数据页,Buffer Pool 快满时又触发 LRU 淘汰脏页,而此时 redo log 刚好写满,三件事撞在一起,几毫秒的语句就卡成几秒。预防的关键不是记住所有参数,而是守住两条底线:每条 UPDATE 都有确定的索引路径,每个高频表的统计信息都保持新鲜。











