PostgreSQL复制槽

前言

相信大家学到这里,基础肯定很不错了。我们知道 wal 文件默认是 16M,wal_keep_size 默认是 0 ,表示不存放额外的 wal 日志,max_wal_size 表示 wal 目录的大小,如果超过了这个大小,有设置了归档的情况下,则会移动到归档目录里。在老版本里面这个参数是wal_keep_segments,表示保留 wal 文件的个数。

如果你看到这里还是不太懂,表示这篇内容你可能还不太适合看,建议先补好基础,当然了你想看我也拦不了你。

如果现在从库异常宕机了,等恢复之后,向主库发送当前需要的 wal record,主库一看傻眼了,这个 wal 已经被归档或者已经被删除了,那怎么办呢?这种情况下只能pg_basebackup 了。

那么主库能不能把还没同步给从库的 wal 文件一直持有着,即使超过了max_wal_size 配置的大小也不删除呢?

有的,它就是我们今天要说的复制槽。

什么是复制槽

复制槽提供了一种办法确保主库不会"删除"还未发送到备库的WAL日志,也不会删除备库需要的多版本,即使备库掉线。

为什么要设置复制槽

复制槽是从 PostgreSQL9.4 引入的,主要是提供了一种自动化的方法来确保主库在所有的备库收到 wal 之前不会移除它们,并且主库也不会移除可能导致恢复冲突的行,即使备库断开也是如此。

在没有启用replication slots的环境中,如果碰到 ERROR: requested WAL segment xxxx has already been removed 的错误,解决办法是要么提前开启了归档,要么重做slave,另外还可以在主库上设置max_wal_size 为更大的值。当然,如果备库停机时间太长,可能主库的WAL日志目录会被撑满,如果设置了复制槽,建议将WAL日志目录放在大容量硬盘上。

工作原理

主库PostgreSQL实例会一直保留预写日志(WAL)文件,直到所有备库所需的插槽都确认已接收到特定段为止。只有完成此操作后,主库实例才会移除相应的WAL文件。

实验过程

复制代码
#主库上创建一个复制槽
SELECT pg_create_physical_replication_slot('test_slot');

#从库的postgres.conf配置后重启
primary_slot_name = 'test_slot'

#此时查询已经有数据了
SELECT * FROM pg_replication_slots WHERE slot_name = 'test_slot';

接下里我们来做个实验

复制代码
#关闭从库,该从库已经有配置了复制槽
docker-compose down postgres-01

#在主库上执行
insert into test values(generate_series(1,1000));
SELECT pg_switch_wal(); 
insert into test values(generate_series(1,1000));
SELECT pg_switch_wal(); 
insert into test values(generate_series(1,1000));
SELECT pg_switch_wal(); 
SELECT pg_switch_wal(); 

此时启动这从库,大家想想会发生什么事情?没错,此时启动从库还是正常的。因为一个 wal 文件大小是 16M,该目录下默认可以存 64 个文件。只是执行四五遍是没办法触发实验结果的。

复制代码
SELECT pg_walfile_name(pg_current_wal_lsn()); 
00000002000000010000009D

#如果你乐意可以多执行几遍
DO $$
BEGIN
  FOR i IN 1..70 LOOP
    PERFORM pg_switch_wal();
    insert into test values(generate_series(1,1000));
    COMMIT;
  END LOOP;
END $$;

SELECT pg_walfile_name(pg_current_wal_lsn()); 
0000000200000001000000E4
#此时已经产生了70个文件了

[root@192 data]# du -sh pg_wal/
2.8G	pg_wal
我们可以看到,此时主库的pg_wal目录远远超过了默认的1G,因为此时从库一直没有反应,主库不敢删除从库没确认的wal

此时启动从库,可以看到复制状态是正常的,没有出现 ERROR: requested WAL segment xxxx has already been removed 的错误。

接下里我们把复制槽给删了,再来做个实验试试看

复制代码
SELECT PID FROM pg_replication_slots WHERE slot_name = 'test_slot';
SELECT pg_terminate_backend(49);
SELECT pg_drop_replication_slot('test_slot');

因为postgresql不允许直接删除活跃的复制槽,所以需要终止后再进行删除
执行70次的日志切换后启动就能看到报错了,从库所需的wal日志已经是被清除了
2025-07-07 16:08:51.718 UTC [34] FATAL:  could not receive data from WAL stream: ERROR:  requested WAL segment 000000020000000300000044 has already been removed
2025-07-07 16:08:51.719 UTC [30] LOG:  waiting for WAL to become available at 3/44000018

相关配置参数和监控

wal_keep_size:当 pg_wal 目录大小超过配置参数max_wal_size 时,提供额外的空间存储 wal 日志。默认是 0 表示不额外存储。pg_wal 目录大小=wal_keep_size+max_wal_size

max_wal_size(12 之后)/wal_keep_segments(12 之前):设置为较大值,保证pg_wal目录下保留较多的wal日志,主库上wal日志留存越多,允许备库宕机的时间越长,设置此参数需要注意不要将pg_wal目录撑满。或者在主库上开启归档,如果没有足够的硬盘空间保留wal归档,至少在备库停机维护时临时开启主库归档。如果备库落后主库wal_keep_segments数量的wal,则主库可能会删除备用服务器仍需要的wal,这种情况下,流复制就会中断。

hot_standby_feedback:备库定时将最小活跃事务ID(xmin)告诉master,使得 master在执行vacuum 时对备库还需要的tuple手下留情,但这样可能会导致主库膨胀,在每个wal_receiver_status_interval定义的周期内发送的频率不超过一次,并且此设置不会覆盖在主数据库上的old_snapshot_threshold行为。

max_standby_streaming_delay:通常会将一些执行时间较长的分析任务、统计SQL跑在备库上。在备库上执行长时间的查询,由于涉及的记录有可能在主库上被更新或删除,主库上对更新或删除数据的老版本进行vacuum后,从库上也会执行这个操作,从而与从库上的当前查询产生冲突。此参数默认为30s,当备库执行SQL时,有可能与正在应用的WAL发生冲突,此查询如果30s没有执行完就被中止,注意30s不是备库上单个查询允许的最大执行时间,是指当备库上应用WAL时允许的最大WAL延迟应用时间,因此备库上查询的执行时间有可能不到这个值就被中止了,此参数可以设置为-1,表示当从库上的WAL应用进程与从库上执行的查询冲突时,WAL应用进程一直等待直到从库查询执行完成。

old_snapshot_threshold:单位为min,最大可以设置为60天,当vacuum回收垃圾时,遇到垃圾记录的xmax大于数据库中现存的最早未提交事务xmin时,不会对其进行回收。因此当数据库中存在很久为结束的事务时,可能会导致数据库膨胀。此参数代表强制删除为过老的事务快照保留的死元组。这会导致长事务读取已被删除的tuple时出错。

vacuum_defer_cleanup_age:指定vacuum延迟清理死亡元组的事务数,vacuum会延迟清除无效的记录,延迟的事务个数通过vacuum_defer_cleanup_age进行设置。默认为0,在主库上设置一个稍大的值也可以减少冲突的发生,但是并不太好计量。

max_standby_archive_delay:备机因为处理归档的wal日志产生查询冲突而取消查询之前的等待时间,和上面的参数类似。

recovery_min_apply_delay:延迟备库设置备库延迟重做WAL的时间,而备库依然及时接收主库发送的WAL日志流,只是不是一接收到WAL后就立即应用,而是等待此参数设置的值再应用。使用此功能将延迟hot_standby_feedback,当synchronous_commit设置为remote_apply时,同步复制也会受此设置的影响,每个commit都需要等待。

总结

我们把之前学的 wal 日志进行了总结,再加上做了实验加深印象,想必大伙都能理解透了,现在就差没有看源码去分析。

我们只需要记住一点,主库会一直保留从库没接收到的 wal 日志。这样子会导致磁盘被撑爆!!!!!以及到时从库起来之后,会有大量的 IO 写,为了赶上主库的进度。

我们这一篇主要讲的是物理复制槽,还有逻辑逻辑流备份和逻辑复制槽我们留着以后再讲。

参考来源:https://www.modb.pro/db/29737

相关推荐
z123456789865 小时前
2026最新两款AI编程工具深度对比实测
java·数据库·ai编程
程序猿DD5 小时前
一个 API Key,统一调用大模型、生图和联网搜索
数据库·网关
TlSfoward6 小时前
抓包代理链路下的 TLS 指纹变化分析 TLSFOWARD抓包工具
数据库·爬虫·网络协议·搜索引擎·php
数智化管理手记6 小时前
账龄分析手工统计易遗漏?自动账龄分析工具怎么搭建
大数据·网络·数据库·人工智能·数据挖掘
LabVIEW开发6 小时前
NI Package Manager安装LabVIEW时InternalServerError错误
网络·数据库·labview·labview知识·labview功能·labview程序
SelectDB6 小时前
PostgreSQL 实时同步到 Apache Doris:一站式 CDC 解决方案
数据库
Database_Cool_7 小时前
AI Agent 长期记忆怎么存?阿里云 PolarDB-X 对接 Mem0 记忆框架实践
数据库·阿里云
会编程的土豆7 小时前
数据库范式
数据库·oracle
L1624767 小时前
MySQL 全环境生产快速安装 + 完整配置手册(整合国产银河麒麟适配 + 主从补充版)
数据库·mysql
yio_yin7 小时前
Spring事务管理(数据一致性)
数据库·sql·spring