CREATE SUBSCRIPTION — 定义一个新的订阅
CREATE SUBSCRIPTIONsubscription_nameCONNECTION 'conninfo' PUBLICATIONpublication_name[, ...] [ WITH (subscription_parameter[=value] [, ... ] ) ]
CREATE SUBSCRIPTION 添加一个新的逻辑复制订阅。创建订阅的用户将成为该订阅的拥有者。订阅名称必须与当前数据库中任何现有订阅的名称不同。
订阅表示到发布者的一条复制连接。因此,除了在本地目录中添加定义之外,该命令通常还会在发布者上创建一个复制槽。
在运行该命令的事务提交时,会启动一个逻辑复制工作进程来为新订阅复制数据,除非该订阅最初被禁用。
要能够创建订阅,你必须拥有 pg_create_subscription 角色的特权,以及当前数据库上的 CREATE 特权。
关于订阅以及逻辑复制整体的更多信息,请参见 Section 29.2 和 Chapter 29。
subscription_name #新订阅的名称。
CONNECTION 'conninfo' #定义如何连接到发布者数据库的 libpq 连接字符串。详细信息请参见 Section 32.1.1。
PUBLICATION publication_name [, ...] #发布者上要订阅的发布的名称。
WITH ( subscription_parameter [= value] [, ... ] ) #该子句指定订阅的可选参数。
以下参数控制订阅创建期间发生的事情:
connect (boolean) #
指定 CREATE SUBSCRIPTION 命令是否应当连接到发布者。默认为 true。将其设置为 false 会强制 create_slot、enabled 和 copy_data 的值为 false。(不能把 connect 设置为 false,同时又把 create_slot、enabled 或 copy_data 设置为 true。)
由于该选项为 false 时不会建立连接,因此不会订阅任何表。要启动复制,你必须手动创建复制槽,在需要时启用故障转移,启用该订阅,并刷新该订阅。示例请参见 Section 29.2.3。
create_slot (boolean) # 指定该命令是否应在发布者上创建复制槽。默认为 true。
如果设置为 false,你需要负责以其他某种方式创建发布者的槽。示例请参见 Section 29.2.3。
enabled (boolean) # 指定该订阅应当主动进行复制,还是只进行设置而不立即启动。默认为 true。
slot_name (string) #要使用的发布者复制槽的名称。默认情况下,使用订阅的名称作为槽名称。
将 slot_name 设置为 NONE 意味着将不会有与该订阅相关联的复制槽。这样的订阅还必须把 enabled 和 create_slot 都设置为 false。当你以后将手动创建复制槽时,可以使用此设置。示例请参见 Section 29.2.3。
当把 slot_name 设置为一个有效名称并把 create_slot 设置为 false 时,被指名槽的 failover 属性值可能与订阅中指定的对应 failover 参数不同。务必确保槽的 failover 属性与订阅的对应参数相匹配,反之亦然。否则,发布者上的槽的行为可能会与这些订阅选项所描述的不同:例如,即使订阅的 failover 选项被禁用,发布者上的槽也可能被同步到后备服务器;或者即使订阅的 failover 选项被启用,发布者上的槽也可能被禁止同步。
以下参数控制订阅创建之后的复制行为:
binary (boolean) #
指定订阅是否请求发布者以二进制格式(而不是文本格式)发送数据。默认为 false。任何初始的表同步拷贝(见 copy_data)也会使用相同的格式。二进制格式可能比文本格式更快,但在不同机器体系结构和 PostgreSQL 版本之间的可移植性较差。二进制格式与数据类型高度相关;例如,它不允许从 smallint 列拷贝到 integer 列,尽管这在文本格式下可以正常工作。即使启用了该选项,也只有具备二进制发送和接收函数的数据类型才会以二进制方式传输。注意初始同步要求所有数据类型都具备二进制发送和接收函数,否则同步将失败(有关发送/接收函数的更多信息请参见 CREATE TYPE)。
在进行跨版本复制时,可能发布者拥有某种数据类型的二进制发送函数,但订阅者缺少该类型的二进制接收函数。在这种情况下,数据传输将会失败,并且不能使用 binary 选项。
如果发布者是 16 之前的 PostgreSQL 版本,那么任何初始表同步都将使用文本格式,即使 binary = true。
copy_data (boolean) # 指定在复制开始时是否复制被订阅发布中已存在的数据。默认为 true。
如果发布包含 WHERE 子句,它将影响被复制的数据。详细信息请参阅 Notes。
请参见 Notes 以了解 copy_data = true 如何与 origin 参数交互的详细信息。
streaming (enum) # 指定是否为该订阅启用进行中事务的流式传输。默认值为 parallel,表示如果有可用的并行应用工作进程,传入的更改会直接通过其中一个来应用。如果没有空闲的并行应用工作进程来处理流式事务,那么这些更改会被写入临时文件,并在事务提交之后被应用。注意,如果并行应用工作进程中发生错误,远程事务的完成 LSN 可能不会记录在服务器日志中。
当发布者和订阅者的模式不同时存在死锁的风险,尽管这种情况很少见。应用工作进程能够自动重试这些事务。
如果设置为 on,传入的更改会被写入临时文件,并且只有在事务在发布者上提交并被订阅者接收之后才会被应用。
如果设置为 off,所有事务都会在发布者上被完整地解码,然后才作为一个整体发送给订阅者。
synchronous_commit (enum) # 该参数的值会覆盖该订阅的各个应用工作进程内的 synchronous_commit 设置。默认值为 off。
对于逻辑复制来说,使用 off 是安全的:如果订阅者因为缺少同步而丢失了事务,数据会从发布者重新发送。
在进行同步逻辑复制时,可能需要不同的设置。逻辑复制工作进程会向发布者报告写入和刷写的位置,并且在使用同步复制时,发布者会等待实际的刷写完成。这意味着当订阅被用于同步复制时,把订阅者的 synchronous_commit 设置为 off 可能会增加发布者上 COMMIT 的延迟。在这种场景下,把 synchronous_commit 设置为 local 或更高可能会更有利。
two_phase (boolean) # 指定是否为该订阅启用两阶段提交。默认为 false。
当启用两阶段提交时,已预备的事务会在 PREPARE TRANSACTION 时被发送给订阅者,并且在订阅者上也被作为两阶段事务处理。否则,已预备的事务只有在提交时才会被发送给订阅者,然后由订阅者立即处理。
两阶段提交的实现要求复制已经成功完成初始表同步阶段。因此,即使为订阅启用了 two_phase,内部的两阶段状态也会暂时保持为 “待定”,直到初始化阶段完成。请查看 subtwophasestate(pg_subscription 的列)来了解实际的两阶段状态。
disable_on_error (boolean) # 指定当订阅工作进程在从发布者复制数据期间检测到任何错误时,是否自动禁用该订阅。默认为 false。
password_required (boolean) # 如果设置为 true,由该订阅产生的到发布者的连接必须使用口令认证,并且口令必须作为连接字符串的一部分被指定。当订阅由超级用户拥有时,该设置会被忽略。默认为 true。只有超级用户才能把该值设置为 false。
run_as_owner (boolean) # 如果为真,所有复制动作都会以订阅拥有者的身份执行。如果为假,复制工作进程会以每个表的拥有者身份对该表执行动作。后一种配置通常安全得多;详细信息请参见 Section 29.11。默认为 false。
origin (string) # 指定该订阅是请求发布者只发送没有发起节点的更改,还是不管发起节点如何都发送更改。将 origin 设置为 none 意味着订阅将请求发布者只发送没有发起节点的更改。将 origin 设置为 any 意味着发布者不管更改的发起节点如何都会发送。默认为 any。
请参见 Notes 以了解 copy_data = true 如何与 origin 参数交互的详细信息。
failover (boolean) # 指定是否启用将与该订阅相关联的复制槽同步到后备服务器,以便在故障转移之后能够从新的主服务器继续逻辑复制。默认为 false。
当指定 boolean 类型的参数时,= value 部分可以被省略,这等价于指定 TRUE。
有关如何在订阅与发布实例之间配置访问控制的详细信息,请参见 Section 29.11。
当创建复制槽时(默认行为),CREATE SUBSCRIPTION 不能在事务块内执行。
创建一个连接到同一数据库集簇的订阅(例如在同一集簇中的数据库之间进行复制,或在同一数据库内进行复制)只有在不作为同一命令的一部分创建复制槽的情况下才会成功。否则,CREATE SUBSCRIPTION 调用将会挂起。要让这种做法可行,请单独创建复制槽(使用函数 pg_create_logical_replication_slot 并将插件名称指定为 pgoutput),然后使用参数 create_slot = false 创建订阅。示例请参见 Section 29.2.3。这是一个实现上的限制,未来的发行版中可能会解除。
如果发布中的任何表带有 WHERE 子句,expression 求值为 false 或 NULL 的行将不会被发布。如果订阅有多个发布、且同一个表以不同的 WHERE 子句被发布,那么只要(与该发布操作相关的)任一表达式得到满足,该行就会被发布。在 WHERE 子句不同的情况下,如果其中一个发布没有 WHERE 子句(与该发布操作相关),或者该发布被声明为 FOR ALL TABLES 或 FOR TABLES IN SCHEMA,则无论其他表达式的定义如何,行总是会被发布。如果订阅者是 15 之前的 PostgreSQL 版本,那么在初始数据同步阶段任何行过滤都会被忽略。对于这种情况,用户可能需要考虑删除那些与后续过滤不兼容的、最初复制的数据。由于初始数据同步在复制现有表数据时不会考虑发布的 publish 参数,因此某些行可能会被复制,而这些行在使用 DML 时是不会被复制的。示例请参见 Section 29.2.2。
不支持包含多个以不同列列表发布同一个表的发布的订阅。
我们允许指定不存在的发布,以便用户以后可以添加它们。这意味着 pg_subscription 中可以有不存在的发布。
当使用 copy_data = true 和 origin = NONE 的订阅参数组合时,初始同步的表数据会直接从发布者复制,这意味着无法得知这些数据的真正来源。如果发布者自己也有订阅,那么被复制的表数据可能源自更上游。这种情况会被检测到,并且会向用户记录一条 WARNING,但该警告只是对潜在问题的一种提示;用户有责任进行必要的检查,以确保被复制数据的来源确实符合预期。
要找出哪些表可能包含非本地来源的数据(由于在发布者上创建的其他订阅),可以尝试以下 SQL 查询:
# substitute <pub-names> below with your publication name(s) to be queried
SELECT DISTINCT PT.schemaname, PT.tablename
FROM pg_publication_tables PT
JOIN pg_class C ON (C.relname = PT.tablename)
JOIN pg_namespace N ON (N.nspname = PT.schemaname),
pg_subscription_rel PS
WHERE C.relnamespace = N.oid AND
(PS.srrelid = C.oid OR
C.oid IN (SELECT relid FROM pg_partition_ancestors(PS.srrelid) UNION
SELECT relid FROM pg_partition_tree(PS.srrelid))) AND
PT.pubname IN (<pub-names>);
创建一个到远程服务器的订阅,它复制发布 mypublication 和 insert_only 中的表,并在提交时立即开始复制:
CREATE SUBSCRIPTION mysub
CONNECTION 'host=192.168.1.50 port=5432 user=foo dbname=foodb'
PUBLICATION mypublication, insert_only;
创建一个到远程服务器的订阅,它复制发布 insert_only 中的表,并且在以后启用之前不会开始复制。
CREATE SUBSCRIPTION mysub
CONNECTION 'host=192.168.1.50 port=5432 user=foo dbname=foodb'
PUBLICATION insert_only
WITH (enabled = false);
CREATE SUBSCRIPTION 是 PostgreSQL 的扩展。