按主键区间分批更新
一条更新语句改动几十万行会有三个后果:事务很大、锁持有时间很长、失败回滚同样漫长。把范围切成按主键推进的小批,每批独立提交,就能把每一步的影响面控制住。切片依据优先选主键或另一个稳定且唯一的递增列,这样每一批的范围明确、可记录、中断后能接着跑。
基本信息
| 适用平台 | 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 单表更新支持 |
| 行位置切片 | 适用于无主键范围的场景 | 位置随更新变化,不能缓存 |
| 按时间区间 | 业务含义清晰 | 时间字段需有索引 |
条件必须能够收敛:筛选条件写错时,每批处理的是同一批行,脚本会陷入死循环。用「影响行数小于批次上限」作为结束条件比按固定总行数循环更稳。
批与批之间的停顿不是多余动作:它让主从复制有机会追平,也给后台清理留出时间。停顿长度按复制延迟实测结果来定。