首页/数据/SQL 速查/分批更新:UPDATE ORDER BY LIMIT 与 ctid 方案
变更与事务52 条

分批更新:UPDATE ORDER BY LIMIT 与 ctid 方案

大表更新采用分批提交:MySQL 支持 UPDATE ... ORDER BY ... LIMIT;PostgreSQL 没有 LIMIT,用 ctid/主键子查询分包。

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

大表分批更新

一条更新语句改几百万行会长时间持锁、生成巨大事务、把复制延迟推高,中途失败还要等回滚。稳妥做法是拆成小批:每次处理固定行数并独立提交,让锁持有时间与单批代价都可控。两种数据库的切片手段不同:MySQL 支持在单表更新上直接排序加限行;PostgreSQL 没有这个语法,需要用物理行位置或主键区间来做切片,并且每批都要重新定位。

基本信息

适用平台MySQL 8.0 / PostgreSQL 12 及以上
MySQL 手段单表更新可带排序与限行,每批一个短事务
PostgreSQL 手段无排序限行语法,用行位置子查询或主键区间切片
排序必要性限定行数时必须同时指定排序,否则取哪批行不确定
节奏控制批与批之间留出间隔,给复制与清理留出余量

典型使用场景

  • 给历史数据补一个字段值,几百万行需要分次处理
  • 状态机迁移:把某个旧状态批量改成新状态
  • 数据清洗:分批修正格式错误的字段值
  • 同步补录:按条件把遗漏标记的同步数据补齐
  • 清理任务:按时间条件分批处理过期数据

两库分批写法

两库分批写法
-- MySQL:单表更新可排序加限行,每批一个短事务
UPDATE orders
SET synced = 1
WHERE synced = 0
ORDER BY id          -- 必须排序,否则取哪批不确定
LIMIT 1000;

-- PostgreSQL:没有排序限行,用行位置子查询切片
WITH batch AS (
    SELECT ctid FROM orders
    WHERE synced = false
    ORDER BY id
    LIMIT 1000
)
UPDATE orders o
SET synced = true
FROM batch b
WHERE o.ctid = b.ctid;

-- 更稳的替代:按主键区间推进,脚本记录进度便于续跑
-- UPDATE orders SET synced = true
-- WHERE synced = false AND id BETWEEN 1 AND 10000;

切片方式对照

切片方式MySQLPostgreSQL
排序限行单表更新支持不支持
行位置切片无对应概念可用行位置子查询
主键区间切片支持,最通用支持,最通用
多表更新不支持排序限行用连接或子查询条件
断点续跑脚本记录已处理区间脚本记录已处理区间
行位置是物理位置,行被更新后会变化。并发写入频繁的场景用主键区间更稳,行位置切片则要求每批都重新计算,不能把位置缓存下来复用。
每批之间加短暂停顿可以让清理线程和被阻塞的写请求有喘息机会。批量大小的经验区间是几百到几千行,具体按单批耗时与锁等待情况调整。

易错点

  • 更新时只写限行不写排序,取到哪一批由执行顺序决定,分批任务的结果不可预期
  • 条件无法收敛:筛选条件写错导致每批处理的是同一批行,循环永不结束
  • 把行位置缓存起来跨批复用,行更新后位置变化,后续批处理漏行
  • 分批节奏过密,复制延迟持续累积,从库查询出现明显延迟
  • 不加断点记录,任务中断后从头再来,做了大量重复工作

数据来源与参考

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