未来星球 未来星球
  • 首页
  • 新闻资讯
  • 技术教程
  • 软件工具
  • 知识百科
  • 生活实用
  • 个人随笔
  • 生命外星人
首页 › 技术教程 › 用Postgres 16逻辑复制做零停机迁移:四个必踩的坑
  • 0
  • 0

用Postgres 16逻辑复制做零停机迁移:四个必踩的坑

admin
22 小时前
5 views
用Postgres 16逻辑复制做零停机迁移:四个必踩的坑-未来星球

零停机迁移最容易出的事故,不是复制断了,是复制看着还在跑,主键却已经开始撞车。逻辑复制不会替你处理序列,这一件事就足够让割接当晚回滚。

下面这份流程来自一次真实的单库迁移:PostgreSQL 16.4,一张 3.2 亿行的订单表,停机窗口要求控制在 90 秒内。它的重点不是”怎么开订阅”,是”怎么保证切换那一刻两边还是同一份数据”。

1. 先把参数改对,否则后面全是白工

逻辑复制依赖 WAL 的逻辑解码,发布端必须是 wal_level=logical。这一项改完要重启,不要在割接前一小时才想起来改。

# 发布端 postgresql.conf(PG 16.4)
wal_level = logical
max_replication_slots = 10      # 至少 = 订阅数 + 预留
max_wal_senders = 10
wal_sender_timeout = 60s

# 订阅端 postgresql.conf
max_logical_replication_workers = 8
max_parallel_apply_workers_per_subscription = 4   # PG 16 才有,决定大事务回放速度
max_sync_workers_per_subscription = 4

# 改完必须重启(reload 不生效)
pg_ctl restart -D /var/lib/pgsql/16/data

订阅端的 max_parallel_apply_workers_per_subscription 是 16 版才引入的。在老版本上照抄这份配置,参数会被直接忽略,你不会收到任何报错。

改完用一条 SQL 确认,别信配置文件:

SHOW wal_level;                 -- 必须 logical
SELECT name, setting FROM pg_settings
 WHERE name IN ('max_replication_slots','max_wal_senders');

2. 发布端:只发布需要的表

不要图省事用 FOR ALL TABLES。它会把你不想迁的日志表、队列表一起带走,而队列表上的高频 UPDATE 会持续放大 WAL 解码压力。

-- 发布端
CREATE PUBLICATION mig_pub FOR TABLE
  public.orders,
  public.order_items,
  public.customers
WITH (publish = 'insert, update, delete, truncate');

-- 确认发布内容
SELECT schemaname, tablename FROM pg_publication_tables
 WHERE pubname = 'mig_pub';

没有主键的表会被拒绝同步。这是逻辑复制最有用的一道保险:它逼你在迁移前补上主键。补不上就先用 REPLICA IDENTITY FULL,代价是每次 UPDATE 都把整行写进 WAL。

3. 订阅端:先建结构,再拉数据

订阅只同步数据,不同步表结构。DDL 必须你自己在两边各跑一遍,这是逻辑复制和物理复制最大的区别。

-- 订阅端:先用 pg_dump 拉结构,不要拉数据
pg_dump -h old-db -U app -s -t public.orders -t public.order_items \
        -t public.customers appdb | psql -h new-db -U app appdb

-- 建立订阅,初始快照会自动 COPY
CREATE SUBSCRIPTION mig_sub
  CONNECTION 'host=old-db port=5432 dbname=appdb user=repl password=***'
  PUBLICATION mig_pub
  WITH (copy_data = true, create_slot = true, enabled = true);

-- 看进度
SELECT srsubstate, count(*) FROM pg_subscription_rel GROUP BY 1;
-- r = ready 才算初始同步完成

初始 COPY 期间,发布端的写入会排在快照之后回放,所以最终一致。但如果这期间有人执行了 TRUNCATE,快照里的数据会和你以为的不一样。

4. 四个必然踩到的坑

前三个坑我在这次迁移里全踩了,第四个是同事在上一家公司踩的。

坑一:序列不同步。逻辑复制不传序列。割接后第一笔 INSERT 会拿到新库从 1 开始的 nextval,直接撞上已有主键。这不是概率问题,是必然发生。

-- 割接前对每个序列执行(在订阅端)
SELECT setval('public.orders_id_seq',
              (SELECT COALESCE(MAX(id), 1) FROM public.orders) + 1000,
              true);
-- 留 1000 的余量,给切换瞬间还在旧库写入的连接

坑二:复制槽把磁盘吃满。订阅端一旦停机超过一天,发布端的 WAL 会为这个槽一直保留。3.2 亿行的库,一天能攒出几百 GB。监控要盯 pg_replication_slots 的 pg_wal_lsn_diff,不是盯订阅状态。

SELECT slot_name, active,
       pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS retained
  FROM pg_replication_slots;

坑三:唯一键冲突让订阅静默停摆。两边数据只要有一行不一致,apply 进程就会报错退出,而 pg_stat_subscription 只显示”没有 worker”。PG 16 起可以查冲突统计:

SELECT * FROM pg_stat_subscription_stats;
-- confl_insert_exists / confl_update_exists 大于 0 就是撞了

坑四:大事务把延迟拉到几小时。一次批量 UPDATE 十万行,在订阅端是串行回放的。迁移期间要和生产约定:单事务不超过 5 万行。否则你会看到延迟曲线在割接前突然翘起来。

5. 切换:把 90 秒拆成四步

真正需要停写的时间只有最后两步。前面都是准备。

  1. 预演三次,每次记录各步骤耗时。第三次的波动应该小于 10 秒。
  2. 停应用写入,等待延迟归零:SELECT * FROM pg_stat_subscription; 直到 received_lsn = latest_end_lsn。
  3. 同步序列(见坑一),然后 ALTER SUBSCRIPTION mig_sub DISABLE; 断开复制。
  4. 切换连接串,放开写入,抽查三张表的 count(*) 与最新 10 条记录。

目录结构建议这样放,切换时不用翻聊天记录:

migration/
├── 00-precheck.sql        # wal_level、主键、磁盘余量
├── 01-schema.sh           # pg_dump -s
├── 02-subscribe.sql       # CREATE SUBSCRIPTION
├── 03-verify.sql          # 行数、校验和、延迟
├── 04-cutover.sql         # 序列对齐 + DISABLE
└── 05-rollback.md         # 回滚步骤,含旧库保留时长

验收不要只看行数。行数相等而内容不同,是这类迁移最常见的假成功。用主键区间做抽样校验和,比全表 count 有用得多。

-- 两边各跑一次,比对结果
SELECT sum(hashtext(id::text || coalesce(amount::text,'') )) AS chk,
       count(*) AS n
  FROM public.orders WHERE id BETWEEN 1000000 AND 1100000;

6. 什么时候不该用这套方案

逻辑复制要求版本不低于 10,跨大版本迁移时类型差异会咬人。从 12 迁到 16,timestamptz 的时区处理、numeric 的精度声明都可能带来偏差。跨两个大版本以上,物理复制加升级检查更稳。

另一个边界是写入模式。如果业务依赖跨库事务或临时表,逻辑复制帮不上忙。它保证的是”最终一致的表数据”,不是”一致的事务边界”。

写在最后

这次迁移最后停机 71 秒,比预算少了 19 秒。省下来的时间不是因为脚本写得好,是因为预演时发现序列那一项要花 40 秒,提前改成了并行执行。

零停机迁移的真正难点,从来不是复制技术,是把”哪些东西不会被复制”列全。序列、DDL、大对象、权限、扩展,五个清单少一项,割接当晚就要补一项。

技术在文档里写得清清楚楚,事故都发生在文档没写的地方。


你做过的那次数据库迁移,最后卡在哪一步?序列、DDL,还是某个谁也没想到的扩展?

0

电梯里为什么装镜子:一条无障碍条款和一个90秒的实验

上一篇

终端模拟器四选一:四个工具,四条不该越过的边界

下一篇

评论 (0)

登录后可发布~
立即登录
暂无评论

admin

当生活给你的全是不如意,那你一定要相信自己的兴趣!
145
文章
0
评论
-11
喜欢
2026 年 10 月
一 二 三 四 五 六 日
 1234
567891011
12131415161718
19202122232425
262728293031  
« 9 月    

近期评论

聚合文章

一座1吉瓦的机房:三代人的电价,和一个不太好接受的推论
火星甲烷之争:一台车测到21ppb,一颗卫星连0.05都没看到
修表师傅把表推回来:一块表,三次开盖,和一个不修的理由
AI新闻日报 – 2026年10月31日:一张82%的安全考卷,和一次两个月的失守
马桶一天漏500升:一份按收益排序的止漏清单
本地跑大模型:4个工具,和3种选错的信号
用Redis 8搭一主两从自动切换:4份配置,和3个必然踩到的坑
钢轨为什么留缝:25米一根,40℃温差,和12毫米的账

猜你喜欢

用 nftables 重写 iptables 规则:一份可复现的迁移对照表

用 nftables 重写 iptables 规则:一份可复现的迁移对照表

admin
11 0 0
2026年AI工具实战指南:7个免费工具让你效率翻倍

2026年AI工具实战指南:7个免费工具让你效率翻倍

admin
86 0 0
普通人如何用AI赚钱?2025最全实操指南

普通人如何用AI赚钱?2025最全实操指南

admin
50 0 0
用 Prometheus 搭一套单机监控:四条告警规则,和三个必然踩到的坑

用 Prometheus 搭一套单机监控:四条告警规则,和三个必然踩到的坑

admin
16 0 0

关于我们

你是否对浩瀚的宇宙充满好奇?你是否渴望掌握未来科技的力量?现在,机会来了!“未来星球”将带你进入奇妙世界,开启你的科技探险之旅! 这是一个充满想象和创造力的博客!

分类导航

首页 新闻资讯 技术教程 软件工具 知识百科 生活实用 个人随笔 生命外星人

友情链接

未来星球AI工具【网络版】 未来星球 pycharm官方站【中国】
Copyright © 2026 未来星球. Designed by nicetheme. 蒙ICP备2026002569号-1
  • 请到 [后台->外观->菜单] 中设置菜单
  • AI未来
  • 生活百科
  • 档案
  • 外星人
  • 未来星球

admin

当生活给你的全是不如意,那你一定要相信自己的兴趣!
145
文章
0
评论
-11
喜欢