bigywater.tech
PostgreSQL · 18

FATAL: requested WAL segment has already been removed:PostgreSQL 流复制、同步复制卡住和复制槽实测

“FATAL: could not receive data from WAL stream: ERROR: requested WAL segment has already been removed 怎么办、同步复制备库挂了主库会怎样” 是流复制运维里最常见的两个问题。这篇在本机自己起一主一备两个 PostgreSQL 16.15 实例,逐个复现并给出复制槽的做法与代价。

0.15 ms
本机回环下单行提交到备库可见的延迟中位数(主库一次性插入 50 万行时 replay_lag 32.3 ms)
264 MB → 4 个段文件
备库停机期间主库写了 264 MB WAL、pg_wal 只剩 4 个段文件,备库回来时 requested WAL segment 已被删除
198 MB
用复制槽后,备库停机期间主库为它保留的 WAL;追平后回到 0 bytes

搭备库

实测 · 本机执行输出
  pg_basebackup 退出码 0 ;standby.signal 存在: True ;postgresql.auto.conf 里的 primary_conninfo: host=127.0.0.1 port=9261(conninfo 里的其余字段略)
  备库启动退出码 0
  备库 pg_is_in_recovery() = True
  主库 pg_stat_replication:state = streaming ,sync_state = async
  在备库上建表: ReadOnlySqlTransaction: cannot execute CREATE TABLE in a read-only transaction

pg_basebackup -R 写好 standby.signal 和 primary_conninfo;备库只读。

复制延迟

实测 · 本机执行输出
  30 次单行提交:备库可见延迟 中位数 0.15 ms,最大 1.10 ms
  主库一次性插入 50 万行后:备库回放落后的字节数最大 31800 KB,约 0.06 s 追平;pg_stat_replication 最后一次的 write_lag / flush_lag / replay_lag = 5.6 ms / 5.6 ms / 32.3 ms

write、flush、replay 三段 lag 分别看;大批量写入时落后的主要是回放。

同步复制

实测 · 本机执行输出
  异步复制:单行提交延迟中位数 0.106 ms
  设了 synchronous_standby_names = '*' 之后 sync_state = sync
  同步复制(remote_write 之上的默认 on):单行提交延迟中位数 0.186 ms
  remote_apply(等备库回放完成):单行提交延迟中位数 0.130 ms
  备库停掉 2 秒后,主库上这条 insert 的提交仍然没有返回: is alive
  pg_stat_activity 里它的状态: ('active', 'IPC', 'SyncRep')
  给它发 cancel 之后:2.0 s 时返回 → returned
      WARNING: canceling wait for synchronous replication due to user request | DETAIL: The transaction has already committed locally, but might not have been replicated to the standby.
  这行数据在主库上已经提交了: 1 行(但没有确认到达备库)

备库停掉后主库的提交会一直卡在 SyncRep;cancel 之后事务在主库已提交但没有备库确认。

WAL 被删除

实测 · 本机执行输出
  备库停机期间主库又写了 264 MB WAL,做了 8 次检查点,主库 pg_wal 里现在只剩 4 个段文件(min_wal_size = 32MB,max_wal_size = 64MB)
  再启动备库,退出码 0
      LOG:  started streaming WAL from primary at 0/<lsn> on timeline 1
      FATAL:  could not receive data from WAL stream: ERROR:  requested WAL segment <segment> has already been removed
      LOG:  started streaming WAL from primary at 0/<lsn> on timeline 1
      FATAL:  could not receive data from WAL stream: ERROR:  requested WAL segment <segment> has already been removed

备库停机太久,主库回收了它需要的 WAL,备库启动报 has already been removed。

复制槽

实测 · 本机执行输出
  备库停机、主库又写入 60 万行并做了 6 次检查点之后:slot s1,active = False,wal_status = extended,为它保留的 WAL = 198 MB
  主库 pg_wal 里现在有 13 个段文件(不设复制槽时只有 4 个)
  再启动备库,退出码 0
  备库追平后 t 的行数: 1900631 ,主库: 1900631
  追平后 slot s1:active = True,保留的 WAL = 0 bytes

复制槽让主库为备库保留 WAL,备库能追平;备库不回来 WAL 会占满磁盘。

分步演示

流复制实测
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / PostgreSQL 16.15(Homebrew);每个实验自己 initdb 一个数据目录,端口 9261 起,只监听 127.0.0.1,不碰本机已有的 5432 上的实例;命令行工具统一用 LC_ALL=C 取英文原文,路径换成 ;计时用 time.perf_counter 或 EXPLAIN ANALYZE 的 Execution Time,数字每次运行会有小幅抖动;实验结束后整个数据目录删除。两个实例:主库 9261、备库 9262,都只监听 127.0.0.1;网络是本机回环,延迟远低于真实网络;为了让 WAL 很快被回收,主库 min_wal_size = 32MB、max_wal_size = 64MB;追加数据时每步做了检查点
    • 没有验证:跨机器网络延迟下的复制延迟;wal_keep_size 作为复制槽的替代;max_slot_wal_keep_size 限制保留量(wal_status 变成 lost 的情况);级联复制;多个同步备库(ANY / FIRST);pg_rewind 让旧主库变备库;promote 的细节;逻辑复制(见下一篇);PostgreSQL 其他大版本
    京ICP备2026067502号-1京公网安备11010502063487号