首页/数据/SQL 速查/按主键分批更新大表
性能与执行计划72 条

按主键分批更新大表

大范围 UPDATE 会长时间持锁、生成大事务:按主键区间循环小批执行,控制锁与回滚规模。

SQL 速查数据更新于 2026-10-13

按主键区间分批更新

一条更新语句改动几十万行会有三个后果:事务很大、锁持有时间很长、失败回滚同样漫长。把范围切成按主键推进的小批,每批独立提交,就能把每一步的影响面控制住。切片依据优先选主键或另一个稳定且唯一的递增列,这样每一批的范围明确、可记录、中断后能接着跑。

基本信息

适用平台MySQL 8.0 / PostgreSQL 12 及以上
切片依据主键或稳定唯一的递增列
提交策略每批独立提交,批间可加短暂间隔
进度记录脚本记录已处理区间,中断后可续跑
前置条件过滤条件列与切片列上要有可用索引

典型使用场景

  • 历史数据批量改状态:几十万行需要分次推进
  • 补字段值:给存量记录补齐新增列的默认内容
  • 数据修正:分批修正格式不一致的字段值
  • 迁移前的数据清理:按主键区间逐段处理
  • 出现长事务告警后拆分更新语句

两库分批写法

两库分批写法
-- MySQL:按主键区间推进,每段一次提交
UPDATE orders SET status = 'archived'
WHERE status = 'done' AND id BETWEEN 1 AND 10000;
-- 循环推进区间,直到影响行数为零

-- MySQL:单表更新也可用排序加限行
UPDATE orders SET status = 'archived'
WHERE status = 'done'
ORDER BY id LIMIT 1000;

-- PostgreSQL:借行位置分批(每批都要重新取位置)
UPDATE orders SET status = 'archived'
WHERE ctid IN (
  SELECT ctid FROM orders WHERE status = 'done' ORDER BY id LIMIT 1000
);

-- 每批之间可加短暂停顿,给复制与清理留出余量
-- 脚本记录已完成的最大主键值,中断后从该值继续

分批策略对照

策略优点注意
主键区间范围明确,可记录,可续跑主键分布不均匀时各批大小差异大
排序加限行每批行数精确可控仅 MySQL 单表更新支持
行位置切片适用于无主键范围的场景位置随更新变化,不能缓存
按时间区间业务含义清晰时间字段需有索引
条件必须能够收敛:筛选条件写错时,每批处理的是同一批行,脚本会陷入死循环。用「影响行数小于批次上限」作为结束条件比按固定总行数循环更稳。
批与批之间的停顿不是多余动作:它让主从复制有机会追平,也给后台清理留出时间。停顿长度按复制延迟实测结果来定。

易错点

  • 一条更新跑全表:长事务、锁等待与复制延迟同时放大,失败回滚更久
  • 切片条件无法收敛,脚本反复处理同一批行,任务永不结束
  • 行位置被缓存跨批复用:行更新后位置变化,导致漏处理
  • 批间没有停顿:复制延迟持续累积,从库查询出现明显延迟
  • 脚本没有进度记录,中断后从头开始,做了大量无效功

数据来源与参考

  • MySQL 8.0 Reference Manual · UPDATE
  • PostgreSQL Documentation · UPDATE
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌