目标
你锁不住一张要重写的一 TB 大表,但你养得起一个孪生表。在原表旁边建分区副本、用触发器把新写入镜像进去、按月有界回填历史、轮询行数差归零,最后在单个短事务里把两张表换名。排他锁只在最后一刻出现且只持续几毫秒,之前的每一步都可重启。
步骤要点
- CREATE TABLE events_p (LIKE events INCLUDING ALL) PARTITION BY RANGE (occurred_at) 建同构分区孪生表。
- 建 AFTER INSERT 触发器把新行同步写入孪生表,迁移窗口内旧表照常服务。
- 按月建空分区并分批 INSERT 回填,每批事务有界、失败可截断该分区重跑。
- 轮询新旧两表 count 差值:触发器持续补新,差值只反映未回填历史,归零即完成。
- 换名前先短暂 LOCK 两表,再在单事务内互改表名,排他锁仅毫秒级。
排错提示
旧表历史行会被 UPDATE 时触发器只覆盖 INSERT 是不够的,要么约定窗口期冻结历史变更,要么补上 ON UPDATE OR DELETE 镜像;ATTACH 校验全分区持锁很久,应“先建空分区再填数”或预建约束让校验跳过;主键没包含分区键会直接建不了唯一索引,开工前先把 occurred_at 纳入主键。切换后保留收益立刻兑现:过期即 DROP 一个分区,秒级完成、不再产生表膨胀。
适用边界
双写触发器在整个迁移窗口增加写开销,日程要紧凑、切换后立即摘除;换名仍需短暂排他锁,哪怕只有几毫秒也应避开流量高峰。
代码示例
-- 1. Twin
CREATE TABLE events_p (LIKE events INCLUDING ALL)
PARTITION BY RANGE (occurred_at);
-- 2. Mirror writes
CREATE TRIGGER events_dw AFTER INSERT ON events
FOR EACH ROW EXECUTE FUNCTION events_dual_write();
-- 5. Swap in one transaction
BEGIN;
LOCK TABLE events, events_p IN ACCESS EXCLUSIVE MODE;
ALTER TABLE events RENAME TO events_old;
ALTER TABLE events_p RENAME TO events;
COMMIT;