根本原因在于事务模型差异:InnoDB需MVCC、行锁、undo log维护一致性,MyISAM仅表锁无事务;前者安全但慢,后者快却易阻塞损坏。

为什么大批量 UPDATE 在 InnoDB 里容易卡住,MyISAM 却“看起来快”?
根本原因不在引擎“快慢”,而在事务模型:InnoDB 是 MVCC + 行锁 + undo log,MyISAM 是表锁 + 无事务。执行 UPDATE 时,InnoDB 要为每一行生成新版本、维护回滚段、检查一致性读视图;MyISAM 直接加表锁、覆盖写磁盘——没有并发保护,自然“快”,但业务一读就阻塞,且崩溃后极易损坏。
- MyISAM 的“快”只适用于只读+离线批量场景,线上业务绝对禁用
- InnoDB 的延迟常来自 undo 表空间不足、buffer pool 淘汰压力大、或长事务阻塞 purge 线程
- 观察
SHOW ENGINE INNODB STATUS中的TRANSACTIONS和ROW OPERATIONS区域能快速定位是锁争用还是 purge 延迟
分批 UPDATE 必须控制的三个参数
不是随便加 LIMIT 1000 就安全。真正影响稳定性的,是单批次的扫描范围、事务大小和 binlog 写入节奏。
-
WHERE条件必须走索引,否则每次LIMIT都要全表扫描前 N 行——2 亿数据下,第 100 批可能比第一批还慢 10 倍 - 单事务建议控制在 5000–50000 行之间:
batch_size = 10000是较稳妥起点,超 10 万易触发innodb_log_file_size不足或主从延时突增 - 每批执行后加
SLEEP(0.1)(非必要但推荐),避免 CPU/IO 持续打满,尤其在主从架构中可缓解 relay log 积压
CASE WHEN 批量更新只适合“几百行”,别硬撑几千
UPDATE ... SET col = CASE WHEN id=1 THEN ... END WHERE id IN (...) 看似优雅,实际有隐性成本:MySQL 会把整个 CASE 表达式编译成内部跳转逻辑,id 列表越长,优化器决策时间越长,且该语句无法利用索引下推(ICP)。
- 适用场景:明确知道要改哪几十到几百条记录,比如运营后台手动勾选的订单状态更新
- 超过 500 个
WHEN分支时,SQL 解析耗时显著上升,错误提示常是Packet too large或直接被max_allowed_packet截断 - 若需更新上万行,务必改用临时表 JOIN 方式,而不是堆长
CASE
pt-online-schema-change 不是用来 UPDATE 字段值的
很多人误以为 pt-osc 能“无锁更新数据”,其实它只解决“改表结构+同步数据”的原子切换,不负责字段内容批量变更。拿它来更新 timeout=0 属于大炮打蚊子,且风险更高:它会在原表上持续挂触发器,一旦触发器执行慢,所有 DML 都会被拖住。
- 真正适合
pt-osc的场景:加索引、改字段类型、删列——即 DDL 类操作 - 字段值批量更新,请回归分批
UPDATE或应用层异步任务,别绕路 - 如果真要用
pt-osc顺带更新数据,只能在--chunk-index对应的影子表INSERT ... SELECT阶段注入逻辑,复杂度陡增,得不偿失
最常被忽略的一点:分批更新前没确认 timeout != 0 是否有索引。没索引的 WHERE 条件,再小的 LIMIT 也是全表扫——2 亿行里找最后一个非零值,可能扫到凌晨。


















