PgSQL Replication Slot 实现分析

本文涉及的产品
云原生数据库 PolarDB MySQL 版,Serverless 5000PCU 100GB
简介: PostgreSQL 9.4在这个版本,我们看到了像Jsonb, Logical Decoding, Replication Slot等新功能。
PostgreSQL 9.4 在这个版本,我们看到了像Jsonb, Logical Decoding,  Replication   Slot 等新功能。对于Replication Slot,文档上介绍的不多,乍一看让人比较难理解是做什么的。其实,Replication Slot的出现,主要是为最终在PG内核实现逻辑复制和双向复制铺路的(目前,逻辑和双向复制在内核中还缺少很多核心功能点,需要借助BDR插件,见PG官方wiki ,引入Replication Slot的背后原因见这里)。不过,作为9.4版本的一个主要功能,它不但可以用于逻辑复制,还可用于物理复制(或者叫Streaming Replication)。针对物理复制的Replication Slot称为Physical Replication Slot。
由于大家目前主要用的还只是PG自带的物理复制方式,我们就重点分析一下Physical Replication Slot。
使用Physical Replication Slot,可以达到两个效果:
A)可以确保从库(standby)需要的日志不被过早备份出去而导致从库失败,出现下面的error:

ERROR: requested WAL segment 00000001000000010000002D has already been removed


通过Replication Slot记录的从库状态,PG会保证从库还没有apply的日志,不会从主库的日志目录里面清除或archive掉。而且,replication slot的状态 信息 是持久化保存的,即便从库断掉或主库重启,这些信息仍然不会丢掉或失效。

B)当允许应用连接从库做只读查询时,Replication Slot可以与运行时参数hot_standby_feedback配合使用,使主库的vacuum操作不会过早的清掉从库查询需要的记录,而出现如下错误(错误的原因下面会详细解释):

ERROR: canceling statement due to conflict with recovery


下面看看Physical Replication Slot的用法和内核实现。

用法
下面是启用Replication Slot的步骤,很简单:
1)首先需要 配置 好Steaming Replication的主库从库。涉及的参数有,listen_addresses(='*'),hot_standby(=on), wal_level(=hot_standby),max_wal_senders(=1),尤其注意配置max_replication_slots大于等于1。这些参数主从库应一致。



2)在主库创建replication slot:
postgres=# SELECT * FROMpg_create_physical_replication_slot('my_rep_slot_1');
slot_name   | xlog_position
---------------+---------------

my_rep_slot_1 |
此时replication slot还不处于active状态。


3) 在从库配置recovery.conf如下,然后重启从库:
standby_mode = 'on'
primary_slot_name = 'my_rep_slot_1'

primary_conninfo = 'user=pg001host=10.x.x.x port=5432 sslmode=prefer sslcompression=1 krbsrvname=postgres'


4)观察主库replication slot的状态变化:
postgres=# SELECT * FROMpg_replication_slots ;
slot_name   | plugin | slot_type |datoid | database | active | xmin | catalog_xmin | restart_lsn
---------------+--------+-----------+--------+----------+--------+------+--------------+-------------

my_rep_slot_1 |        | physical  |       |          | t      | 1812 |              | 0/3011A70


5)与hot_standby_feedback配合使用。在将从库的postgresql.conf文件中的hot_standby_feedback选项设为on,重启从库即可。

内核实现

replication slot是由下面的patch加入内核中的:
author            RobertHaas < rhaas@postgresql.org >        
Sat, 1 Feb 2014 03:45:17 +0000 (22:45-0500)
committer      RobertHaas < rhaas@postgresql.org >        
Sat, 1 Feb 2014 03:45:36 +0000 (22:45-0500)
Replication slots are a crash-safe datastructure which can be created
on either a master or a standby to preventpremature removal of
write-ahead log segments needed by astandby, as well as (with
hot_standby_feedback=on) pruning of tupleswhose removal would cause
replication conflicts.  Slots have some advantages over existing

techniques, as explained in thedocumentation.


这个patch改的文件不少,分析这些代码,我们重点关注下面的 问题
A)Replication Slot是如何在内核中创建的?
通过分析创建Replication Slot时调用的函数ReplicationSlotCreate,可以看出, Replication Slot实质上是内存中的一些数据结构,加上持久化保存到pg_replslot/<slot name>目录中的二进制状态文件。 在PG启动的时候,预先在共享内存中分配好这些数据结构所用内存(即一个大小为max_replication_slots的数组)。这些数据结构在用户创建Replication Slot时开始被使用。一个Replication Slot被创建并使用后,其数据结构和状态文件会被WAL(Write-Ahead-Log)的发送者(wal_sender)进程更新。
另外,如果单纯从Replication Slot的名字,我们很容易觉得Replication Slot会创建新的与从库的连接,进行日志发送。实际上,创建过程中并没有创建新的与从库的连接,Replication Slot还是使用了wal_sender原有连接(由于一个从库一个wal_sender连接,所以一个从库和主库之间也只有一个active的Replication Slot)。
B) Replication Slot的状态是如何被更新的?
很容易发现,Replication Slot的状态的更新有两种情况。
第一种是在ProcessStandbyHSFeedbackMessage这个函数被更新。这个函数是在处理wal_sender所收到的从库发回的feedback reply message时调用的。通过这个函数,我们可以看出,每个wal_sender进程的Replication Slot(就是用户创建的Replication Slot)保存在MyReplicationSlot这个全局变量中。在处理从库发回的reply时,reply中的xmin信息会被提取出来,存入slot的data.xmin和effective_xmin域中,并通过函数ProcArraySetReplicationSlotXmin,最终更新到 系统 全局的procArray->replication_slot_xmin结构中(以使其对所有进程可见),完成slot的更新。
这里要注意,如果我们有多个Replication Slot(分别对应各 从库),则在更新全局结构procArray->replication_slot_xmin时,会选取所有slot中最小的xmin值。
第二种是在ProcessStandbyReplyMessage中。这个函数处理从库发送的restart lsn信息(即从库apply的日志的编号),会直接将其更新到replication slot的restart lsn域中,并保存到磁盘,用于主库判断是否要保留日志不被archive。
C) Replication Slot如何和hot_standby_feedback配合,来避免从库的查询冲突的?
这里,从库的查询冲突指的是下面的情况:从库上有正在运行的查询,而且运行时间很长;这时主库上在做正常的vaccum,清除掉无用的记录版本。但主库的vaccum是不知道从库的查询存在的,所以在清除时,不考虑从库的正在运行的查询,只考虑主库里面的事务状态。其结果,vacuum可能会清除掉从库查询中涉及的,仍然在使用的记录版本。当这些vaccum操作,通过日志同步到从库,而恰好从库的查询仍然没有运行完,vaccum就要等待或cancel这个查询,以保证同步正常继续和查询不出现错误的结果。这样,每当用户在从库运行长查询,就容易出现我们上面提到到query conflict error。
如何避免这种冲突 ?目前最好的解决方案是使用hot_standby_feedback + Replication Slot。其原理简单说就是,从库将它的查询所依赖的记录版本的信息,以一个事务id来表示,并放在从库发回给主库wal_sender的reply中发给主库(见函数XLogWalRcvSendHSFeedback),并最终传导给主库vaccum,让其刀下留人,暂时不清除相关记录。
具体过程是,在从库,函数XLogWalRcvSendHSFeedback调用GetOldestXmin获得xmin,放入给主库的reply中。主库的wal_sender收到后,如果使用了Replication Slot,就把这个xmin放入slot的状态信息中,并更新此时系统所有slot的最小xmin。这个系统所有slot的最小xmin怎么在主库传导给vacuum的呢?以自动触发的vacuum操作为例,其中的逻辑的顺序如下:
GetSnapshotData(vacuum事务开始时,获取slot xmin,存入全局变量) ->vacuum_set_xid_limits(调用 GetOldestXmin,通过全局变量,获取系统xmin和slot xmin,取较小值)-> vacuum_lazy (使用xmin,判断哪些记录版本可以清除)
这样,利用Replication Slot这个渠道,就解决了从库查询冲突。


注意事项
最后,介绍一下使用Replication Slot的注意事项:
1)如果收不到从库的reply,Replication Slot的状态restart lsn会保持不变,造成主库会一直保留本地日志,可能导致日志磁盘满。所以应该实时监控日志磁盘使用情况,并设置较小的wal_sender_timeout,及早发现从库断掉的情况。
2)将hot_standby_feedback设为on时,注意如果从库长时间有慢查询发生,可能导致发回到主库的xmin变化较慢,主库的vaccum操作停滞,造成主库被频繁更新的表大小暴增。

除了物理复制,Replication Slot对逻辑复制的意义更大,我们期待在可能出现逻辑复制功能的9.5版本中看到它大显身手。
相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
目录
相关文章
|
9月前
|
监控 关系型数据库 数据库
监控复制:PG_STAT_REPLICATION
监控复制:PG_STAT_REPLICATION
95 0
|
关系型数据库 MySQL
MySQL Group Replication
MySQL Group Replication
70 0
|
搜索推荐 关系型数据库 分布式数据库
【DB吐槽大会】第7期 - PG slot 无failover
大家好,这里是DB吐槽大会,第7期 - PG slot 无failover
|
SQL Oracle 关系型数据库
xDB Replication Server - PostgreSQL, Oracle, SQL Server, PPAS 全量、增量(redo log based, or trigger based)同步(支持single-master, mult-master同步, 支持DDL)
xDB Replication Server - PostgreSQL, Oracle, SQL Server, PPAS 全量、增量(redo log based, or trigger based)同步(支持single-master, mult-master同步, 支持DDL)
773 0
|
SQL 关系型数据库 MySQL
|
关系型数据库 MySQL 数据库
Mysql group replication复制原理
Mysql group replication复制原理
4090 0
|
关系型数据库 MySQL
MySQL5.7 & AliSQL5.6 半同步复制 rpl_semi_sync_master_wait_point参数
MySQL5.5 开始支持半同步复制,但会有数据丢失的风险。MySQL5.7 中进行了优化,通过 rpl_semi_sync_master_wait_point 参数来实现。
5149 0