天天看点

PostgreSQL 10.0 逻辑复制原理与最佳实践

postgresql , logical replication , 逻辑复制 , 最佳实践

postgresql 从2010年发布的9.0开始支持流式物理复制,备库可以作为只读库打开,提供给用户使用。

1. 物理层面完全一致,这是许多商业数据库的惯用手段。例如oracle的dg。

2. 延迟低,事务执行过程中产生redo record,实时的在备库apply,事务结束时,备库立马能见到数据。不论事务多大,都一样。

3. 物理复制的一致性、可靠性达到了金融级的需求,不必担心数据逻辑层面不一致。

但是物理复制要求主备块级完全一致,所以有一些无法覆盖的应用场景,例如备库不仅要只读,还要可写。又比如备库不需要完全和主库一致,只需要复制部分数据,或者备库要从多个数据源复制数据,等等。

1. 数据库实例的部分,例如单个数据库或者某些表的复制需求。

例如某个游戏业务,账号体系是一套数据库,如果全国各地有多个接入点,全部都连到中心数据库进行认证可能不太科学。那么就希望将登陆需要用到的一些数据表同步到多个数据中心,而不是整个数据库实例。

2. 数据到达subcriber后,针对不同数据,设置触发器。

3. 将多个数据库实例的数据,同步到一个目标数据库。

例如多个数据库同步到一个大的数据仓库。

4. 在不同的数据库版本之间,复制数据

5. 将一个数据库实例的不同数据,复制到不同的目标库。

例如省级数据库的数据,按地区划分,分别复制到不同的地区。

6. 在多个数据库实例之间,共享部分数据。

例如某个业务按用户id哈希,拆分成了8个数据库,但是有些小的维度表,需要在多个数据库之间共享。

以上场景是物理复制无法覆盖的。

逻辑复制应运而生,实际上,从2014年发布的9.4版本开始,postgresql就支持逻辑复制了,只是一直没有将其引入内核。

2017年即将发布的10.0,将会在内核层面支持基于redo流的逻辑复制。

另一个好消息是,你可以针对同一个数据库实例,同时使用逻辑复制和物理复制,因为他们都是基于redo的。

下面我们来看一下逻辑复制的概念、架构、监控、安全、最佳实践。

postgresql 逻辑复制是事务级别的复制,引入了几个概念

发布者指数据上游节点,你需要将哪些表发布出去?

上游节点需要配置这些东西

1. 需要将数据库的redo的wal_level配置为logical。

2. 需要发布逻辑复制的表,必须配置表的replica identity,即如何标示老的记录。

被复制的表,建议有pk约束。

解释

什么是system table?

指catalog或者user_catalog_table = true的表,不允许修改列的数据类型。

3. output plugin

发布者还需要一个output plugin,将redo按发布的定义,解析成需要的格式,等待订阅者的订阅。

<a href="https://www.postgresql.org/docs/devel/static/logicaldecoding-output-plugin.html">https://www.postgresql.org/docs/devel/static/logicaldecoding-output-plugin.html</a>

是不是有点像这个呢?

<a href="https://github.com/digoal/blog/blob/master/201408/20140828_01.md">《postgresql 闪回 - flash back query emulate by trigger》</a>

创建发布

修改发布

1. 目前仅仅支持发布表,不允许发布其他对象。

2. 同一张表,可以发布多次。

3. 在同一个数据库中,可以创建多个publication,但是不能重名,通过系统表查看已创建的publication

4. 允许使用all tables发布所有表。

5. 一个publication允许有多个订阅者。

6. 目前publication仅支持insert, update, delete。

7. 允许发布时,选择发布insert、update、delete,比如只发布insert,而不发布update, delete。

8. 当发布了表的update, delete时,表必须设置replica identity,即如何标示old tuple,通过pk或者uk或者full。如果设置了nothing,则执行update,delete时会报错

报错例子

9. create publication或者alter publication,发布或者修改发布内容中添加或者删除表时,都是事务级别,不会出现复制了部分事务的情况。 so the table will start or stop replicating at the correct snapshot once the transaction has committed。

10. 发布者需要设置wal_level=logical,同时开启足够的worker,设置足够大的replication slot,设置足够多的sender。

因为每一个订阅,都要消耗掉一个replication slot,需要消耗一个wal sender,一个worker进程。

发布者的pg_hba.conf需要设置replication条目,允许订阅者连接。

发布者的数据库中,必须有replication角色的用户,或者超级用户,并且订阅者要使用它通过流复制协议连接到发布者。

订阅者,需要指定发布者的连接信息,以及 publication name,同时指定需要在publication数据库中创建的slot name。

在同一个数据库中,可以创建多个订阅。

订阅者和发布者的角色可以同时出现在同一个实例的同一个数据库中。

创建订阅

修改订阅

1. 订阅者需要通过流复制协议连接到发布者,同时需要在发布者创建replication slot。

因此发布者的pg_hba.conf中需要配置相应的replication条目,允许订阅者通过流复制协议连接。

同时连接发布者的用户,必须具备replication权限,或者具备超级用户权限。

2. 同一个数据库中,可以创建多个subscription,这些subscription可以连自一个或多个发布者。

3. 当同一个数据库中有多个subscription时,如果这些subscriptions是来自同一个发布者,那么他们之间发布的表不能重叠。

也就是说,订阅者的同一张表,不能接受来自同一个源的多个发布。

例如

发布者

订阅者

a表接受了同一个源的多次发布,会报错。

4. 每一个订阅,都需要在发布端创建一个slot,可以使用slot name = ?指定,或者默认为subscription name。

即使是同一个发布端,只要订阅了多次,就需要创建多个slot,因为slot中记录了同步的lsn信息。

这种情况,对于这个订阅者,建议合并成一个,例如

5. pg_dump导出数据库逻辑数据时,默认不会导出subscription的定义,除非使用选项 --include-subscriptions

6. 在创建subscription或者alter subscription时,可以使用enable来启用该订阅,或者使用disable暂停该订阅。

7. 如果要完全删除订阅,使用drop subscription,注意,删除订阅后,本地的表不会被删除,数据也不会清除,仅仅是不在接收该订阅的上游信息。

这个也很好理解,因为同一个表可能接收多个订阅。删订阅和删表是两码事。

8. 删除订阅后,如果要重新使用该订阅,数据需要resync,比如订阅的上游节点有100万数据,resync会将这100万数据同步过来。随后进入增量同步。

将来10.0正式发布时,也许会提供一个选项,选择要不要resync。

(目前来说,一次订阅,意味着这些被订阅的表会和发布端一模一样(只要发布端发布了insert,update,delete语句)。如果发布端只发布了insert,那么源表的update和delete不会被订阅)

9. 订阅时,不会自动创建发布端的表,所以表需要在订阅端先创建好。

将来10.0正式发布时,也许会填补这个功能。

目前发布端和订阅端的表定义必须完全一致,包括

schema,表名必须一致。

字段名和字段类型必须一致。

字段顺序可以不一致。

除了表,其他对象都不能被订阅,例如你不能将表订阅到一张视图中。

10. 必须使用超级用户创建订阅

逻辑复制,本质上是事务层级的复制,需要在订阅端执行sql。

如果订阅端执行sql失败(或者说引发了任何错误,包括约束等),都会导致该订阅暂停。

注意,update, delete没有匹配的记录时,不会报错,也不会导致订阅暂停。

用户可以在订阅端数据库日志中查看错误原因。

1. 通过修改订阅端的数据,解决冲突。例如insert违反了唯一约束时,可以删除订阅端造成唯一约束冲突的记录先delete掉。然后使用alter subscription name enable让订阅继续。

2. 在订阅端调用pg_replication_origin_advance(node_name text, pos pg_lsn)函数,node_name就是subscription name,pos指重新开始的lsn,从而跳过有冲突的事务。

当前的lsn通过pg_replication_origin_status.remote_lsn查看。

<a href="https://www.postgresql.org/docs/devel/static/view-pg-replication-origin-status.html">https://www.postgresql.org/docs/devel/static/view-pg-replication-origin-status.html</a>

PostgreSQL 10.0 逻辑复制原理与最佳实践
PostgreSQL 10.0 逻辑复制原理与最佳实践

1. 在创建subscription后,订阅者会在发布端创建一个快照,同时将发布端的数据,在同一个快照内的视角,发送给订阅端。

例如订阅了发布端的a,b,c三张表,那么这三张表的当前快照下的数据,会发送给订阅端。

2. 订阅端接收完快照后,发布端会从快照的这个lsn开始,从wal(redo)日志中,根据发布定义的表以及过滤条件(insert\update\delete),按事务组装复制的消息包,通过流复制协议发送给订阅端的apply(wal receiver)进程。

3. 订阅端接收到消息包之后,对于同一个订阅(wal reciever或applyer进程)来说,会按照事务的先后顺序,按事务apply。所以在订阅端,apply也是事务一致的。

将来可能会考虑组复制,提高并发性。

ps

其实你可以把不同的表分别放在不同的订阅中,这样就是并行的了。但是消耗的wal sender进程与连接会多一些。

4. 在订阅端,wal receiver(applyer)的session_replication_role会设置为replica。

这个影响数据库的trigger和rule。

参考

<a href="https://github.com/digoal/blog/blob/master/201506/20150615_01.md">《postgresql trigger/rule based replication configure, disable/enable [ replica | always ] trigger | rule》</a>

<a href="https://github.com/digoal/blog/blob/master/201102/20110209_01.md">《can session_replication_role used like mysql's blackhole engine?》</a>

逻辑复制依旧使用的是2010年推出的流复制协议,所以监控手段差别不大。

订阅端视图pg_stat_subscription

每一个subcription有一条记录,一个订阅可能有多个active subscription workers。

对于一个已激活(enabled)的订阅,对应有1个apply进程,所以在这个视图中有一条记录。

一个暂停或者crash的订阅,在这个视图中不会有记录。

发布端

1. 必须设置pg_hba.conf,允许订阅端通过流复制连接发布端

2. wal_level必须设置为logical,记录逻辑复制的一些额外信息

3. 订阅端配置的conninfo中,发布端的角色必须具备replication权限,或者超级用户权限

4. 使用某个用户在某个数据库中创建publication,这个用户必须对该数据库具备create权限。

订阅端

1. 订阅端创建subscription的用户,必须是超级用户

权限检测仅仅在连接发布端的时候,后期不会检测,比如从发布端获取数据,或者apply数据时,不再检测是否为超级用户。

1. wal_level=logical

2. max_replication_slots,每一个订阅需要消耗一个slot,每一个指定了slot的流式物理复制也要消耗一个slot。

3. max_wal_senders,每一个slot要使用一个wal sender,每一个流式物理复制也要使用一个wal sender。

4. max_worker_processes,必须大于等于max_wal_senders加并行计算进程,或者其他插件需要fork的进程数。

1. max_replication_slots,大于等于该实例总共需要创建的订阅数

2. max_logical_replication_workers,大于等于该实例总共需要创建的订阅数

3. max_worker_processes, 大于等于max_logical_replication_workers + 1 + cpu并行计算 + 其他插件需要fork的进程数.

1. 部署postgresql 10.0

2. 规划数据目录

3. 配置环境变量

4. 初始化数据库

5. 配置postgresql.conf

6. 配置pg_hba.conf

7. 配置发布端

查看当前数据库有哪些发布

订阅端创建成功后,可以查看发布端的slot

8. 配置订阅端

查看整个数据库集群有哪些订阅

9. 小事务压测

观测延迟

发布端decode, output plugin压力较大,写入tps达到18万时,延迟较严重

订阅端还可以查看pg_replication_origin_status,

remote_lsn指已复制到订阅者的发布者的lsn。

local_lsn指本地已持久化redo的lsn,只有在这个lsn之前的dirty page才能flush到磁盘。

对应的数据结构

延迟较大

物理流复制几乎0延迟,而逻辑复制带来的延迟是比较大的。

10. 大事务压测

实际上postgresql的逻辑复制也是流式的,事务没有结束时,发布端事务过程中产生的变更会实时的同步并在订阅端apply。

但是逻辑复制始终是基于行的,延迟和块级复制还是差很大。

订阅端如果开启了异步提交,那么哪些脏页(shared buffer)能flush到磁盘呢?

查看pg_replication_origin_status,

local_lsn指本地已持久化redo的lsn,只有在这个lsn之前的dirty page才能flush到磁盘。(这样做可以保证可靠性和一致性)

1. 如果被订阅的数据有主外键约束,请将其作为一个订阅。否则可能会有约束的问题

2. 因为逻辑复制在订阅端实际上是逻辑写操作,请尽量避免锁冲突。比如truncate , alter table.

3. 在订阅端,delete, update 不存在记录,不报错

<a href="https://www.postgresql.org/docs/devel/static/logical-replication.html">https://www.postgresql.org/docs/devel/static/logical-replication.html</a>