本文档适用于 PostgreSQL 9.4 9.6
翻译:亭亭小次郎
pglogical是PostgreSQL 的拓展模块, 为PostgreSQL数据库提供了逻辑流复制发布和订阅的功能。 pglogical重用了BDR项目中的一部分相关技术.
我们使用的下列术语来描述节点和数据流之间的关系,重用了一些早期的Slony技术中的术语:
- 节点 - PostgreSQL数据库实例
- 发布者和订阅者 - 节点的角色名称
- 复制集 - 关系表的集合
pglogical是新技术组件,使用了最新的PostgreSQL 数据库中的一些核心功能,所以存在一些数据库版本限制:
- 数据源发布和订阅节点需要运行 PostgreSQL 9.4 +
- 复制源过滤和冲突检测需要 PostgreSQL 9.5 +
支持的使用场景:
- 主版本数据库之间的升级(存在上述的版本限制)
- 完整的数据库复制
- 利用复制集,选择性的筛选的关系表
- 可从多个上游服务器,做数据的聚集和合并
架构上的细节︰
- pglogical 工作在每个数据库层面上,而不是像物理流复制一样工作在整个数据库集簇实例级别
- 一个发布程序提供给多个订阅者不会引起额外的磁盘写开销
- 一个订阅服务器可以从几个起源的更改合并和检测与自动和可配置冲突决议 (一些,但并不是所有方面所需的多主机)的更改之间的冲突。
- 级联复制是在变更集转发的过程中实现的。
1.安装要求
要使用 pglogical 提供的发布和订阅组件, 数据库服务器必须运行PostgreSQL 9.4 或更高版本。
pglogical_output 扩展需要提供程序和订阅服务器上安装。没有实际创建扩展是必需的它必须只会出现在 PostgreSQL 安装。
在提供程序和订阅服务器上必须安装 pglogical 扩展。你必须创建扩展 pglogical 两侧。
提供程序和订阅服务器上的表必须具有相同的名称,而且必须在相同的架构。未来的修订可能添加映射功能。
提供程序和订阅服务器上的表必须具有相同的列,每列相同的数据类型。 CHECK CONSTRAINTS 不空约束等必须相同或较弱(更加宽容)比提供商订阅服务器上。
表必须有主键相同。它不被建议添加附加唯一约束以外主要关键(见下文)。
"限制和约束"章节中会提到一些额外的要求。
2.使用
本节介绍了pglogical 扩展模块复制的基本用法。
2.1.快速安装
首先 PostgreSQL服务器必须正确配置才能够支持逻辑解码︰
wal_level = 'logical'
max_worker_processes = 10 # one per database needed on provider node
# one per node needed on subscriber node
max_replication_slots = 10 # one per node needed on provider node
max_wal_senders = 10 # one per node needed on provider node
shared_preload_libraries = 'pglogical'
如果你想要处理解决与上一次/第一次更新之间的冲突 wins(参阅冲突章节), 你的数据库版本需要为PostgreSQL 9.5+ (在9.4中无效) 您可以向 PostgreSQL.conf 添加此额外的选项:
track_commit_timestamp = on # needed for last/first update wins conflict resolution
# property available in PostgreSQL 9.5+
pg_hba.conf 需要配置成允许从本地主机复制,用户拥有有复制权限,连接权限。
下一步 在所有节点上安装pglogical拓展模块:
CREATE EXTENSION pglogical;
创建节点连接提供服务:
SELECT pglogical.create_node(
node_name := 'provider1',
dsn := 'host=providerhost port=5432 dbname=db'
);
默认将会把所有public 模式下表都添加到默认复制集。
SELECT pglogical.replication_set_add_all_tables('default', ARRAY['public']);
或者你还可以创建额外复制集并向其中添加表 (见复制设置下面)。
它是通常最好创建复制集之前订阅,在初始复制安装在单一的初始交易过程中同步所有表。然而,大数据库的用户可能希望创建它们以增量方式更好地控制。
一旦该提供程序节点是安装程序,用户可以订阅它。 首先需要须创建订阅服务器节点
SELECT pglogical.create_node(
node_name := 'subscriber1',
dsn := 'host=thishost port=5432 dbname=db'
);
并且最后在订阅服务器节点上,您可以创建订阅,将在后台启动同步和复制过程︰
SELECT pglogical.create\_subscription(
subscription\_name := 'subscription1',
provider\_dsn := 'host=providerhost port=5432 dbname=db'
);
2.2 节点管理
节点可以使用动态SQL接口添加和移除。
pglogical.create_node (node_name dsn text) 创建一个节点。
参数: node_name-新节点的名称,只有一个节点被允许每个数据库 dsn 连接字符串应该是供应商的节点的节点,这应该是从外面可到达
- pglogical.drop_node (node_name name ,ifexists bool) 移除 pglogical 节点。
参数: node_name -名称的现有节点 ifexists -如果值为 true ,当订阅不存在时,不会引发错误,默认值为 false
当创建了一个节点时,它创建的接口也在 create\_node 中指定的 dsn 与节点相同的名称。此接口允许将替代接口与不同的连接字符串添加到现有的节点。
参数︰
>> node\_name-名称的现有节点
>> interface\_name 是界面的一个新要添加的名称
>> dsn - 用于新接口的连接的节点的 数据源名称
* pglogical.alter\_node\_drop\_interface(node\_name interface\_name) 从节点中移除现有接口。
参数︰
>> node\_name 现在有节点的名称
>> replication_set 现有的接口的名称
## 2.3 订阅管理
* pglogical.create\_subscription(subscription\_name name, provider\_dsn text, replication\_sets text[], synchronize\_structure boolean, synchronize\_data boolean, forward\_origins text[])
从当前节点到提供程序节点创建订阅。命令不会阻塞,只是将启动操作。
参数:
> subscription\_name-订阅的名称必须是唯一的
> provider\_dsn-一个提供程序的连接字符串
>> replication\_sets-复制数组设置订阅,这些必须已经存在,默认值是 ```"{default,default_insert_only}"```
>> synchronize\_structure 指定如果要同步到订阅服务器,默认值为假从提供程序结构
>> synchronize\_data 指定如果将数据同步从供应商到订阅服务器上,默认值
>> forward\_origins 阵列的原产地名称来转发,目前唯一受支持的值是空数组意味着不要转发在提供程序节点上,并不是起源的任何更改或"{}",是指复制所有的变化,不管是什么是他们的起源,默认是"{}"
* pglogical.drop_subscription(subscription_name name, ifexists bool) 断开连接订阅,并从目录中移除。
参数:
> subscription\_name-现有订阅的名称
>> ifexists-如果为 true,
当订阅不存在,则不会引发错误,默认值为 false
* pglogical.alter\_subscription\_disable (subscription\_name 的名字,立即 bool) 禁用订阅并断开提供程序。
参数︰
subscription\_name-现有订阅的名称
立即-如果为 true,则订阅立即停止,否则为它将只停在当前事务结束,默认值为 false
* pglogical.alter\_subscription\_enable (subscription\_name 的名字,立即 bool) 启用禁用订阅。
参数︰
subscription\_name-现有订阅的名称
立即-如果为 true,则立即开始预订,否则为它将只开始在当前事务结束,默认值为 false
* pglogical.alter\_subscription\_interface (subscription\_name 的名字,interface\_name 名称) 切换订阅以使用不同的接口连接到提供程序节点。
参数︰
subscription\_name-现有订阅的名称
interface\_name-现有接口的当前提供程序节点名称
* pglogical.alter\_subscription\_synchronize (subscription\_name 名称,截形 bool) 在单个操作中同步所有同步的表在所有集。表复制和同步一个接一个。命令不会阻止,只是将启动操作。
参数︰
subscription\_name-现有订阅的名称
截形-如果为 true,则表将被截断之前副本,默认值为假
* pglogical.alter\_subscription\_resynchronize\_table (subscription\_name 姓名、 关系 regclass) 重新同步一个现有的表。__警告:此函数将首先截断表。__
参数︰
subscription\_name-现有订阅的名称
选择合格的关系-现有表的名称
* pglogical.show\_subscription\_status (subscription\_name name) 显示订阅的状态和基本信息。
参数︰
subscription\_name-现有订阅,没有名字时提供,该函数将显示状态为所有订阅本地节点上的可选名称
* pglogical.show\_subscription\_table (subscription\_name name, relation regclass) 显示同步状态的表。
参数︰
subscription\_name-现有的订阅的名称
relation 存在的表的名称, (需要合适)
* ``` pglogical.alter_subscription_add_replication_set(subscription_name name, replication_set name) ```
添加一个复制到订阅服务器设置。不同步,只有激活消费事件。
参数:
subscription\_name-现有订阅的名称
replication\_set-复制设置为添加名称
* ```pglogical.alter_subscription_remove_replication_set(subscription_name name, replication_set name) ```
从订阅服务器上的配置中删除一个复制。
参数:
>> subscription\_name -现有订阅的名称
>> replication\_set -要删除的复制集合
## 2.4 复制集
复制集提供一种机制来控制将复制的数据库中的表和这些表上的哪些操作将被复制。
每个复制的集可以单独指定插入(INSERTs)、 更新(UPDATEs)、 删除(DELETEs)和 截断TRUNCATEs 等在集合上的操作
将被复制。
每个表可以在多个复制集和每个订阅服务器可以订阅多个复制集以及。
结果集的表和复制操作是集合的并集的表是在。
没被添加进入复制集合中的表, 将不会被复制。
有两个预先存在复制集名为"default"和"default\_insert\_only"。
"default"复制集定义要复制到表中的所有更改。
"default\_insert\_only"只复制插入和没有主键的表(见限制的详细信息一节)。
管理复制集提供了以下函数管理:
* ```
pglogical.create_replication_set(set_name name, replicate_insert bool, replicate_update bool, replicate_delete bool, replicate_truncate bool)
``` 该函数用来创建一个新的复制集合
参数:
>> set\_name -集合的名称必须是唯一的
>> replicate\_insert -指定是否复制插入,默认值为 true
>> replicate\_update -指定是否复制更新,默认值为 true
>> replicate\_delete -指定是否复制删除,默认值为 true
>> replicate\_truncate -指定是否复制截断,默认值为 true
* pglogical.alter\_replication\_set (set\_name name,replicate\_inserts bool,replicate\_updates bool,replicate\_deletes bool,replicate\_truncate bool) 此函数用来更改已经存在的复制集合的参数。
参数︰
>> set\_name-现有复制名称
>> replicate\_insert-指定是否复制插入,默认值为 true
>> replicate\_update-指定是否复制更新,默认值为 true
>> replicate\_delete-指定是否复制删除,默认值为 true
>> replicate\_truncate-指定是否复制截断,默认值为 true
* pglogical.drop\_replication\_set(set\_name text) 删除复制设置。
参数︰
```set_name``` -名称的现有复制设置
* ```pglogical.replication_set_add_table(set_name name, relation regclass, synchronize_data boolean)```
将关系表添加到复制集合中。
参数︰
>> set_name -的现有复制设置的名称
>> relation - 要添加到组的 关系的名称或表的OID
>> ```synchronize_data`` -如果为 true,在所有的订阅服务器的订阅,给出了复制套默认假上同步表数据,默认值为false
* pglogical.replication\_set\_add\_all\_tables (set\_name name,schema\_names text[],synchronize\_data boolean) 添加所有表中给定的架构。唯一的现有表添加,将在以后创建的表将不会自动添加。有关如何确保在将来创建的表都添加正确复制设置,请参阅复制集自动分配新表。
参数︰
>> set\_name-名称的现有复制设置
>> schema\_names-数组的名称名称的现有架构应该从中添加表
>> synchronize\_data-如果为 true,在所有的订阅服务器的订阅,给出了复制套默认假上同步表数据
* pglogical.replication\_set\_remove\_table (set\_name name、relation regclass) 从复制集中删除的表。
参数︰
set\_name-名称的现有复制设置
关系的名称或表的 OID 从组中删除
* pglogical.replication\_set\_add\_sequence (set\_name name,relation regclass synchronize\_data boolean) 将表添加到复制集合中。
参数︰
set\_name-的现有复制设置的名称
关系的名称或序列的 OID 要添加到组
synchronize\_data-如果为 true,则序列值将会立即同步,默认值为false
* pglogical.replication\_set\_add\_all\_sequences (synchronize\_data schema\_names text[],set\_name boolean) 添加所有序列在给定的架构。只有现有序列补充,将在未来创造任何序列将不会自动添加。
参数︰
set\_name-名称的现有复制设置
schema\_names-数组的名称名称的现有架构应该从中添加表
synchronize\_data-如果为 true,则序列值将会立即同步,默认值为假
* ```pglogical.replication_set_remove_sequence(set_name name, relation regclass)
从复制集中删除的表。
参数︰ set_name-名称的现有复制设置 关系的名称或序列的 OID 从组中删除
您可以查看哪些表在其中设置通过查询 pglogical.tables 视图的信息。
###2.4.1 自动为新的数据表配置复制集合
事件触发器设施可以用于描述定义新创建的表的复制集的规则。
示例:
CREATE OR REPLACE FUNCTION pglogical_assign_repset()
RETURNS event_trigger AS $$
DECLARE obj record;
BEGIN
FOR obj IN SELECT * FROM pg_event_trigger_ddl_commands()
LOOP
IF obj.object_type = 'table' THEN
IF obj.schema_name = 'config' THEN
PERFORM pglogical.replication_set_add_table('configuration', obj.objid);
ELSIF NOT obj.in_extension THEN
PERFORM pglogical.replication_set_add_table('default', obj.objid);
END IF;
END IF;
END LOOP;
END;
$$ LANGUAGE plpgsql;
CREATE EVENT TRIGGER pglogical_assign_repset_trg
ON ddl_command_end
WHEN TAG IN ('CREATE TABLE', 'CREATE TABLE AS')
EXECUTE PROCEDURE pglogical_assign_repset();
上面的示例中, 将放入复制集配置在架构配置中创建的所有新表和其他不由扩展创建的所有新表将会以默认复制设置。
2.5 附加功能
- pglogical.synchronize_sequence(relation regclass)
推序列状态到所有订阅服务器。不同于订阅和表同步函数,此函数应该在提供程序上运行。它还会强制更新跟踪的序列状态,将消耗所有的订阅服务器 (复制集筛选仍然适用) 一旦他们复制的事务已在其中执行此函数。
参数︰ -relation 现有序列,合适的关系表的名称
3.冲突
如果节点订阅多个提供程序,或当本地写入在订阅服务器上发生,可能会发生冲突,尤其是对传入的变化。这些都自动检测,并可以就此采取行动取决于配置。
解决冲突的办法是通过配置 pglogical.conflict_resolution 参数。
pglogical.conflict_resolution 支持的配置参数选项为︰
- error - 复制将停止上错误如果检测到冲突和手动操作需要解决
- apply_remote - 总是应用与本地数据有冲突的更改,这是默认值
- keep_local - 保留数据的本地版本,并忽略来自远程节点相互冲突的更改
- last_update_wins - 时间戳为提交最新的版本(newest commit timestamp)的数据将会被保存(这可以是本地或远程版本)
- first_update_wins - 时间戳为最旧的版本(oldest timestamp)的数据将会被保存(这可以是本地或远程版本)
当参数track_commit_timestamp被禁用时,唯一允许的配置值是 apply_remote。 PostgreSQL 9.4 不支持 track_commit_timestamp 配置参数只能配置参数apply_remote(该参数是默认值)。
4.限制和约束
4.1 超级用户是需要
目前pglogical复制需要超级用户。后期的版本中可能需要用户拥有复制权限。
4.2 UNLOGGED 和临时不复制
UNLOGGED(未记录LOG)的表和临时表将不能被复制,这与物理流复制一样。
4.3 每次复制一个数据库
要复制多个数据库必须为每个单独建立发布和订阅关系的配置 。 一次性在 PostgreSQL 安装配置复制的所有数据库没实现。
4.4 数据表需要主键或复制标志
对于缺乏一个主键或其他有效的复制标识的表(例如缺少唯一约束的表),不能复制更新和删除(UPDATE 和DELETE操作)。 由于不存在唯一约束的话,复制没办法查找对应的需要更新或删除的元组。
请[阅读] (http://www.postgresql.org/docs/current/static/sql-altertable.html#SQL-CREATETABLE-REPLICA-IDENTITY) ,你可以了解到关于复制的更多详细信息。
4.5 只有一个唯一索引/约束/PK
如果多个上游配置或下游接受本地写入然后只有一个唯一索引应在上下游复制表。 解决冲突因此冲突行 会触发ERROR,如果行满足主键, 但在下游的一侧违反唯一约束。 这将导致复制停止,直到下游表进行修改直至违反去除掉。
如果上游的存在唯一约束,下游只从这个上游获取写,没有其他的数据源,复制会正常工作。 规则是下游的约束不能比那些在上游(s)的限制性更强。
4.6 DDL
不支持自动 DDL 复制。这样的提供程序和订阅服务器上的数据库保持兼容管理 DDL 是用户的责任。
pglogical 提供了 pglogical.replicate_ddl_command 函数,从而允许 DDL 在提供程序和一个一致点在订阅服务器上运行。
##4.7 没有复制队列刷新
那里不是支持冻结交易主机上的和等待,直到所有挂起队列中复制事务重播从插槽。 为使上游支持只读模式,这将在未来的版本中加入。
这意味着必须注意当应用表结构的变化。 如果有已提交的事务不尚未复制和提供程序和订阅服务器上的表结构改变了方式,使符合排队的事务复制订阅服务器表同时将停止。
管理员也应确保对数据库主的写操作进行架构更改前停止, 或者使用 pglogical.replicate_ddl_command 函数来对数据库结构进行更改, 用来保证他们在副本上重演之后的一致点。
一旦多主机复制支持添加然后然后使用 pglogical.replicate_ddl_command
将是不够的作为订阅服务器可能生成新的复制事务与旧的结构后架构更改犯在发布服务器上。 用户必须确保写入在所有节点上停了下来,所有插槽都连接之前进行架构更改。
##4.8 外键
外的键约束不会强制执行复制过程 -即使违反外键约束,上游服务起到订阅服务器的复制会成功执行。
4.9 截断
使用TRUNCATE ... CASCADE将仅适用于上游使用CASCADE选项。
(妥善处理这很可能需要在PostgreSQL中增加了关于TRUNCATE CASCADE支持外键)。
TRUNCATE ...不支持RESTART IDENTITY。数据库重启TRUNCATE不会复制到副本。
4.10 序列
序列加到复制集的状态被实时周期性和不被复制。 使用动态缓冲区的值被复制,以使用户真正得到序列的未来状态。 这样可以减少序列的 last_value
落后用户的概念的机会, 但并没有完全消除这种可能性。
它可能需要调用 synchronize_sequence,以确保所有订阅服务器有最新的信息关于给定序列数据库等数据加载或在线升级过程中的"大事件"之后。
它是一般建议使用 bigserial 和 bigint 类型序列在多节点系统上较小的序列可能达到快速序列空间的尾部。
用户想要独立序列在提供程序和订阅服务器可以避免将序列添加到复制组和创建序列与步骤间隔等于或大于节点的数目。然后在每个节点上设置不同的偏移量。增量的选项用于创建序列或改变序列,并使用 setval(...) 设置的起始点。
4.11 触发器
应用进程和初始复制进程都运行与 session_replication_role 设置为复制副本,
这意味着这个 ENABLE 副本和 ENABLE ALWAYS 总是会被使用。
4.12 PostgreSQL 版本差异
pglogical 支持跨 PostgreSQL 主要版本之间的复制。 尽管如此, 长期跨版本复制不是考虑的设计目标,尽管它可能会正常工作。 更改在提供程序上有效, 但在订阅服务器上不同版本之间进行复制时,可能会出现问题。
它是支持从旧版本复制到新版本因为 PostgreSQL 的向后兼容性保证的,但只有有限的向前兼容性比较安全。
不同的次要版本之间的复制, 没有任何区别。
4.13 不复制 DDL
逻辑解码并不直接解码目录更改。所以该插件不能随便发送 CREATE TABLE 语句,当添加一个新的表。
如果被解码的数据应用于另一个 PostgreSQL 数据库然后其表定义必须保持同步通过一些手段外部逻辑解码插件对其自我,如︰
事件触发器使用 DDL deparse 捕获 DDL 更改,当他们发生,并把它们写到一个表来复制并应用的另一端;或 做 DDL 管理通过同步 DDL 的所有节点的工具
5.pglogical和BDR不同之处?
pglogical 基于技术为 BDR 开发和股份与 BDR 的一些代码。 它的设计要比 BDR 更灵活,应用更好地为单主机单向复制,数据收集/合并、非网格多主机的拓扑结构,等等。
它忽略了在 BDR 中找到一些功能:
网格的多主机。多主机支持有限与解决冲突存在,但必须单独添加相互复制连接。
分布式的全局序列。改用每个节点上不同序列的偏移量。
DDL复制。用户必须保持表定义一致本身。pglogical 提供队列函数来帮助这一点。
全局DDL锁。有没有 DDL 复制就没有全局的锁定要求...只适用于表但这就带来了相互的多主机复制问题。请参阅下一个点。
全局刷新到一致状态。BDR的DDL锁的一部分是一步由防新复制事务从插入所有节点队列正在犯, 然后刷新到对等节点。 这样可以确保一旦表结构发生了变化,不会在应用的队列中出现没有复制的事务。 pglogical不支持多主之间的复制, (只支持双节点之间的单向复制),请参见"限制"。
有关更多信息,请参见"限制和约束"。
它还添加了一些额外的功能︰
- 连接节点之间的低耦合;支持级联的逻辑复制; 拓扑结构并不局限于像BDR的网状结构配置。
- 重新用于其他项目的松散耦合输出插件
- JSON数据流输出可以用于事务检查
pglogical主要目的是提供一个更加整洁、更加简单的基础组件, 使用pglogical, PostgreSQL不需要打补丁。 pglogical是一个可插拔的和可扩展的PostgreSQL模块。