PG中的逻辑复制(Logical Replication)

0    1930    9

Tags:

👉 本文共约14170个字,系统预计阅读时间或需54分钟。

逻辑复制简介

从v9.4开始,PostgreSQL正式支持了Logical Decoding,也就是逻辑解码,逻辑解码的实现为最终实现逻辑复制奠定了坚实的基础,终于在v10大版本发布之后,PostgreSQL正式支持了原生的逻辑复制,在此之前可以使用pglogical、BDR等插件实现,pglogical有着更强大的冲突处理能力。BDR特点是支持多主机、DDL复制、全局序列、全局DDL锁,最早开源过一阵子,后面就闭源了。

Postgresql支持“逻辑”和“物理”两种复制方式,逻辑复制是一种基于数据对象的复制标识(通常是主键)复制数据对象及其修改的方法,而后者使用准确的块地址以及逐字节的复制方式(WAL日志)。逻辑复制允许数据复制和安全性上更细粒度的控制(可以对特定表进行复制)。逻辑复制采用发布者->订阅者模型,具体流程结构图如下:

逻辑复制(Logical Replication)是对现有PostgreSQL复制特性的一种扩展。是一种实现表级数据复制的特性。

逻辑复制使用一种发布订阅模型,其中有一个或者更多订阅者订阅一个发布者节点上的一个或者更多发布 。订阅者从它们所订阅的发布拉取数据并且可能后续重新发布这些数据以允许级联复制或者更复杂的配置。

一个表的逻辑复制通常开始于对发布者服务器上的数据取得一个快照并且将快照拷贝给订阅者。一旦这项工作完成,发布者上的更改会被实时发送给订阅者。订阅者以与发布者相同的顺序应用那些数据,这样在一个订阅中能够保证发布的事务一致性。这种数据复制的方法有时候也被称为事务性复制。

  • 逻辑复制是PostgreSQL V10重量级新特性,支持内置的逻辑复制
  • 在10版本之前,虽然没有内置的逻辑复制,也可以通过其它方式实现,触发器、自定义脚本实现表级别同步,另外也可以通过外部工具 Londiste3 实现
  • 从2014年发布的9.4版本开始,PostgreSQL就支持逻辑复制了,只是一直没有将其引入内核
  • 可以针对同一个数据库实例,同时使用逻辑复制和物理复制,因为他们都是基于WAL的

备注:Londiste 是一个较新的基于队列的复制软件,它基于触发器来驱动,它是SkyTools项目集的一部分,该软件可在这个网址找到 http://pgfoundry.org/projects/skytools/

逻辑复制的典型用法是

  • 在一个数据库或者一个数据库的子集中发生更改时,把增量的改变发送给订阅者。
  • 在更改到达订阅者时引发触发器。
  • 把多个数据库联合到单一数据库中(例如用于分析目的)。
  • 在PostgreSQL的不同主版本之间进行复制。
  • 在不同平台上(例如Linux到Windows)的PostgreSQL实例之间进行复制。
  • 将复制数据的访问给予不同的用户组。
  • 在多个数据库间共享数据库的一个子集。

订阅者数据库的行为与任何其他PostgreSQL实例相同,并且可以被用作其他数据库的发布者,只需要定义它自己的发布。当订阅者被应用当作只读时,单一的订阅中不会有冲突。在另一方面,如果应用或者对相同表集合的订阅者执行了其他的写动作,冲突可能会发生。

角色

img

  1. Walsender:使用OutputPlugins进行逻辑解码,然后将解码后的数据发送给订阅端
  2. logical replication launcher:类似于autovacuum launcher,守护进程,进行fork logical replication worker,发布端和订阅端都有该进程
  3. logical replication worker:工作进程,接收Walsender发送过来的数据

发布

发布可以被定义在任何物理复制的主服务器上。定义有发布的节点被称为发布者。发布是从一个表或者一组表生成的改变的集合,也可以被描述为更改集合或者复制集合。每个发布都只存在于一个数据库中。

发布与模式不同,不会影响表的访问方式。如果需要,每个表都可以被加入到多个发布。当前,发布只能包含表。对象必须被明确地加入到发布,除非发布是用ALL TABLES创建的。

Publication可以选择把它们产生的更改限制为INSERTUPDATEDELETE以及TRUNCATE的任意组合,类似于触发器如何被特定事件类型触发的方式。默认情况下,所有操作类型都会被复制。

为了能够复制UPDATEDELETE操作,被发布的表必须配置有一个“复制标识”,这样在订阅者那一端才能标识对于更新或删除合适的行。默认情况下,复制标识就是主键(如果有主键)。也可以在复制标识上设置另一个唯一索引(有特定的额外要求)。如果表没有合适的键,那么可以设置成复制标识“full”,它表示整个行都成为那个键。不过,这样做效率很低,只有在没有其他方案的情况下才应该使用。如果在发布者端设置了“full”之外的复制标识,在订阅者端也必须设置一个复制标识,它应该由相同的或者少一些的列组成。如何设置复制标识的细节请参考REPLICA IDENTITY。如果在复制UPDATEDELETE操作的发布中加入了没有复制标识的表,那么订阅者上后续的UPDATEDELETE操作将导致错误。不管有没有复制标识,INSERT操作都能继续下去。

每一个发布都可以有多个订阅者

Publication通过使用CREATE PUBLICATION命令创建并且可以在之后使用相应的命令进行修改或者删除。

表可以使用ALTER PUBLICATION动态地增加或者移除。ADD TABLE以及DROP TABLE操作都是事务性的,因此一旦该事务提交,该表将以正确的快照开始或者停止复制。

订阅

订阅是逻辑复制的下游端。订阅被定义在其中的节点被称为订阅者。一个订阅会定义到另一个数据库的连接以及它想要订阅的发布集合(一个或者多个)。

订阅者数据库的行为与任何其他PostgreSQL实例相同,并且可以被用作其他数据库的发布者,只需要定义它自己的发布。

如果需要,一个订阅者节点可以有多个订阅。可以在一对发布者-订阅者之间定义多个订阅,在这种情况下要确保被订阅的发布对象不会重叠。

每一个订阅都将通过一个复制槽(见第 26.2.6 节)接收更改。预先存在的表数据的初始数据同步过程可能会要求额外的临时复制槽。

逻辑复制订阅可以是同步复制(见第 26.2.8 节)的后备服务器。后备名称默认是该订阅的名称。可以在订阅的连接信息中用application_name指定一个可供选择的名称。

如果当前用户是一个超级用户,则订阅会被pg_dump转储。否则订阅会被跳过并且写出一个警告,因为非超级用户不能从pg_subscription目录中读取所有的订阅信息。

可以使用CREATE SUBSCRIPTION增加订阅,并且使用ALTER SUBSCRIPTION在任何时刻停止/继续订阅,还可以使用DROP SUBSCRIPTION删除订阅。

在一个订阅被删除并且重建时,同步信息会丢失。这意味着数据必须被重新同步。

模式定义不会被复制,并且被发布的表必须在订阅者上存在。只有常规表可以成为复制的目标。例如,不能复制视图。

表在发布者和订阅者之间使用完全限定的表名进行匹配。不支持复制到订阅者上命名不同的表。

表的列也通过名称匹配。订阅表中的列顺序不需要与发布表中的顺序一样。 列的数据类型也不需要一样,只要可以将数据的文本表示形式转换为目标类型即可。 例如,您可以从integer类型的列复制到bigint类型的列。 目标表还可以具有发布表中不存在的额外列。额外列都将使用目标表的定义中指定的默认值填充。

冲突

逻辑复制的行为类似于正常的DML操作,即便数据在订阅者节点本地被修改,逻辑复制也会根据收到的更改来更新数据。如果流入的数据违背了任何约束,复制将停止。这种情况被称为一个冲突。在复制UPDATEDELETE操作时,缺失的数据将不会产生冲突并且这类操作将被简单地跳过。

冲突将会产生错误并且停止复制,它必须由用户手工解决。在订阅者的服务器日志中可以找到有关冲突的详细情况。

通过更改订阅者上的数据(这样它就不会与到来的数据发生冲突)或者跳过与已有数据冲突的事务可以解决这种冲突。通过调用pg_replication_origin_advance()函数可以跳过该事务,函数的参数是对应于该订阅名称的node_name以及一个位置。复制源头的当前位置可以在pg_replication_origin_status系统视图中看到。

复制槽

简介

复制槽提供了一种自动化的方法来确保主控机在所有的后备机收到 WAL 段 之前不会移除它们,并且主控机也不会移除可能导致 恢复冲突的行,即使后备机断开也是如此。

复制槽分为物理复制槽physical replication slot和逻辑复制槽logic replication slot。

物理复制槽

物理复制槽一般结合流复制一起使用,能够很好的保证备库需要的日志不会在主库删除。

创建物理复制槽:

查询:

删除复制槽:

如果报错:

ERROR: replication slot "my_rep_slot_1" is active for PID 679

注意:活跃状态的slot不能删除

逻辑复制槽

Logic replication slots一般被用于逻辑异步复制,一个很好的应用是用于异构数据库之间的逻辑复制。

大致原理是将源端xlog进行解码,解析成具体sql,然后到目标端进行回放。支持逻辑解码需要将wal_level设置成logic,logic会在replica的基础上增加一些信息以支持逻辑编码,该模式会增大wal日志的数量,尤其是大量的updata、delete操作的库。

创建logic slot与physical slot函数不同:

创建时需要指定逻辑复制插槽名称和输出插件:

注意:

1)逻辑复制插槽名称:每个逻辑复制插槽都有一个名称,创建时需要指定名称,可以包含小写字母、数字和下划线

2)输出插件:创建逻辑复制插槽时需要指定输出插件,输出插件有:test_decoding、pgoutput、wal2json等

test_decoding 输出插件 :PostgreSQL 9.4+原生附带了 test_decoding 输出插件($PG_HOME/lib 目录下对应有 test_decoding.so),如果您的消费者支持 test_decoding ,则可以使用 test_decoding 输出插件。

pgoutput 输出插件 :PostgreSQL 10+原生附带了 pgoutput 输出插件($PG_HOME/lib 目录下对应有 pgoutput.so),如果您的消费者支持 pgoutput ,则可以使用 pgoutput 输出插件。pgoutput插件输出的是二进制的数据

wal2json 输出插件 :wal2json 是另一个流行的逻辑解码输出插件,PostgreSQL原生不携带、需要数据库服务单独安装插件才能使用。wal2json输出的是json格式的数据

查询:

函数pg_logical_slot_get_changes和pg_logical_slot_peek_changes

pg_logical_slot_get_changespg_logical_slot_peek_changes 是 PostgreSQL 中与逻辑复制相关的函数,用于从逻辑复制插槽(logical replication slot)中获取数据变更(changes)的操作。

  1. pg_logical_slot_get_changes查询并删除数据。仅在第一次返回结果,多次调用可能会返回空结果集,这意味着当get命令执行时,结果会被提供和删除,这增强了我们编写使用这些事件创建表副本的逻辑的能力。
    • 这个函数用于从逻辑复制插槽中获取数据变更,并将其返回。
    • 它通常会获取并删除插槽中的数据,以确保不会多次获取相同的数据。
    • 这是一个阻塞操作,它将等待新的数据变更可用时才会返回结果。
  2. pg_logical_slot_peek_changes只查询不删数据。多次调用每次都会返回相同的结果。是另一个 PostgreSQL 命令,用于在不使用 WAL 条目的情况下查看更改。
    • 这个函数也用于从逻辑复制插槽中获取数据变更,但与 pg_logical_slot_get_changes 不同,它只是查看插槽中的数据而不会删除它。
    • 这是一个非阻塞操作,如果没有可用的数据变更,它会立即返回空结果。

这些函数通常与逻辑复制一起使用,逻辑复制允许将数据库的变更(插入、更新、删除等)从一个 PostgreSQL 数据库复制到另一个,通常用于数据复制、数据仓库、备份和其他用例。这两个函数允许消费者(如订阅者)从逻辑复制插槽中获取并处理变更,以保持目标数据库与源数据库的同步。 pg_logical_slot_get_changes 通常用于实际处理变更,而 pg_logical_slot_peek_changes 用于查看可用的变更而不对其进行处理。

示例:

其它

作为复制槽的替代,也可以使用wal_keep_size阻止移除旧的 WAL 段,或者使用archive_command把段保存在一个归档中。 不过,这些方法常常会导致保留的 WAL 段比需要的更多,而复制槽只保留已知所需要的段。 另一方面,复制槽可以保留很多的WAL段以至于它们填满了分配给pg_wal的空间; max_slot_wal_keep_size限制复制槽所保留的WAL文件的大小。

类似地,hot_standby_feedbackvacuum_defer_cleanup_age保护了相关行不被 vacuum 移除,但是前者在后备机断开期间无法提供保护,而后者则需要被设置为一个很高 的值已提供足够的保护。复制槽克服了这些缺点。

查询和操纵复制槽

每个复制槽都有一个名字,名字可以包含小写字母、数字和下划线字符。

已有的复制槽和它们的状态可以在 pg_replication_slots 视图中看到。槽可以通过流复制协议(见第 52.4 节) 或者 SQL 函数(见第 9.27.6 节)创建并且移除。

注意:复制槽是针对PG实例的, pg_replication_slots 可以查出整个PG实例上的复制槽。

pg_replication_slots 视图提供了数据库集群上当前存在的所有复制插槽(replication slots)的列表及其当前状态,参数如下:

Name Type ReferencesDescription

slot_name name 复制插槽名称,为集群范围内的唯一标识符。

plugin name 正在使用的包含逻辑槽输出插件的共享对象的基本名称,即逻辑解码输出插件名(例如:test_decoding、pgoutput、wal2json),对于物理插槽则为空。

slot_type text 插槽类型:logical 代表逻辑插槽,physical 代表物理插槽。

datoid oid 该插槽所关联的数据库的 OID,只有逻辑插槽才具有关联的数据库,物理插槽则没有、即为空。

database text 该插槽所关联的数据库的名称,只有逻辑插槽才具有关联的数据库,物理插槽则没有、即为空。

temporary boolean PG 10开始支持该参数。是否为临时插槽。创建插槽时,如果声明此 replication slot 为临时插槽,则不会存在磁盘、会在失败后删除。

active boolean 如果此插槽当前正在使用,则为真。

active_pid integer 对应的流复制进程ID。如果当前正在使用插槽(活跃),则使用此插槽的会话的进程的 PID。如果不活动则为空。

xmin xid 此插槽需要数据库保留的最早事务。VACUUM 无法删除任何后来的事务删除的元组。

catalog_xmin xid 这个插槽要需要数据库保留的影响系统目录的最旧事务。VACUUM不能移除被其后续事务删除的目录元组。

restart_lsn pg_lsn 可能仍被这个插槽的消费者要求的最旧的 WAL 地址(LSN),并且因此不会在检查点期间自动被移除。

confirmed_flush_lsn pg_lsn 代表逻辑插槽的消费者已经确认接收数据到什么位置的地址(LSN)。比这个地址更旧的数据已经不再可用。对于物理槽这里为空。

wal_status text PG 13开始支持该参数。此插槽声称的 WAL 文件的可用性。

可能的值为:

reserved 意味着声称的文件包含 max_wal_size。

extended 意味着 max_wal_size 已超出,但文件仍保留,通过复制插槽或 wal_keep_size。

本人提供Oracle(OCP、OCM)、MySQL(OCP)、PostgreSQL(PGCA、PGCE、PGCM)等数据库的培训和考证业务,私聊QQ646634621或微信dbaup66,谢谢!
AiDBA后续精彩内容已被站长无情隐藏,请输入验证码解锁本文!
验证码:
获取验证码: 请先关注本站微信公众号,然后回复“验证码”,获取验证码。在微信里搜索“AiDBA”或者“dbaup6”或者微信扫描右侧二维码都可以关注本站微信公众号。

标签:

Avatar photo

小麦苗

学习或考证,均可联系麦老师,请加微信db_bao或QQ646634621

您可能还喜欢...

发表回复