PostgreSQL 支持 COPY 操作,COPY 操作通过流复制协议(Streaming Replication Protocol)实现。COPY 命令允许在服务器之间进行高速批量数据传输,有三种流复制模式:
COPY-IN 和 COPY-OUT 操作分别将连接切换到一个不同于正常命令处理的子协议中,该子协议一直持续到操作完成;不同于 COPY-IN 和 COPY-OUT 模式,COPY-BOTH 模式是从狭义上定义的流复制协议,我们将上述统一称为流复制协议。
当服务器端收到并执行 COPY FROM STDIN SQL 语句时,将启动 COPY-IN 模式(从客户端向服务器传输数据)。服务器端向客户端发送 CopyInResponse 消息。然后,客户端应该发送 0 个或多个 CopyData 消息,形成一个输入数据流。消息边界与行边界保持一致看似是一个合理的选择,但实际上它们没有任何关系,也不是必需的。客户端可以通过发送 CopyDone 消息(允许成功终止)或 CopyFail 消息(将导致 COPY SQL 语句执行失败并返回错误)来终止 COPY-IN 模式。然后,服务器端将恢复到 COPY 启动前的正常命令处理模式——简单查询协议或扩展查询协议流程。服务器端接下来将发送 CommandComplete(如果执行成功)或 ErrorResponse(如果执行失败)。如果在执行 COPY-IN 模式期间服务器端检测到错误(包括收到 CopyFail 消息),将发出 ErrorResponse 消息。如果 COPY 命令是通过扩展查询消息发出的,则服务器端将立即丢弃客户端消息,直到收到 Sync 消息,然后服务器端将发出 ReadyForQuery 消息并返回正常处理。如果 COPY 命令是在简单查询的 Query 消息中发出的,则该消息的其余部分将被丢弃,服务器端发出 ReadyForQuery 消息。在前述任何一种情况下,客户端发出的任何后续 CopyData、CopyDone 或 CopyFail 消息都将被丢弃。服务器端在 COPY-IN 模式中将忽略接收到的 Flush 和 Sync 消息。服务器端接收到任何其他非复制消息类型都会被算作一个错误,该错误将中止上述的 COPY-IN 状态。需要注意的是,Flush 和 Sync 作为例外情况是为了方便客户端库,客户端总是在 Execute 消息后发送 Flush 或 Sync,而不检查要执行的命令是否为 COPY FROM STDIN。当服务器端接收并执行 COPY TO STDOUT SQL 语句时,将启动 COPY-OUT 模式(从服务器向外传输数据)。服务器端向客户端端发送一条 CopyOutResponse 消息,然后是 0 条或多条 CopyData 消息(总是每行一条),后跟 CopyDone 消息。然后,服务器端恢复到 COPY 启动前的命令处理模式,并发送 CommandComplete。客户端无法中止传输(除非关闭连接或发出 Cancel 请求),但它可以丢弃不需要的 CopyData 和 CopyDone 消息。如果服务器端在 COPY-OUT 模式期间检测到错误,将发出 ErrorResponse 消息并恢复到正常处理模式。客户端应将收到 ErrorResponse 视为终止 COPY-OUT 模式。NoticeResponse 和 ParameterStatus 消息可以散布在 CopyData 消息之间;客户端必须处理这些情况,并应为其他异步消息类型做好准备。除此之外,除 CopyData 或 CopyDone 之外的任何消息类型都可能被视为终止 COPY-OUT 模式。COPY-BOTH 是一种与 COPY 相关的模式,它允许在服务器之间或服务器与客户端之间进行高速批量数据传输。当处于 Walsender 模式中的服务器端执行 START_REPLICATION 语句时,将启动 COPY-BOTH 模式。服务器端向客户端发送 CopyBothResponse 消息。然后,服务器端和客户端都可以发送 CopyData 消息,直到任意一端发送 CopyDone 消息。客户端发送 CopyDone 消息后,连接将从 COPY-BOTH 模式切换到 COPY-OUT 模式,并且客户端不可以再发送任何 CopyData 消息。类似地,当服务器发送 CopyDone 消息时,连接将切换到 COPY-IN 模式,并且服务器不可以再发送任何 CopyData 消息。当服务器端和客户端双方都发送了 CopyDone 消息后,COPY 模式将终止,服务器端将恢复到正常的命令处理模式。如果服务器端在 COPY-BOTH 模式期间检测到错误,将发出 ErrorResponse 消息,丢弃客户端消息,直到收到 Sync 消息,然后发出 ReadyForQuery 消息并返回正常处理模式。客户端应将收到 ErrorResponse 视为终止双向复制;在这种情况下,不应发送 CopyDone 消息。CopyInResponse、CopyOutResponse 和 CopyBothResponse 消息包含向客户端通知每行的列数以及每列使用的格式代码的字段。在目前的实现中,给定 COPY 操作中的所有列都将使用相同的格式,但消息格式的设计并不做这样的假设。当需要启动流复制时,客户端端会在 StartupMessage 消息中发送 replication 参数。replication 参数的布尔值 true(或 on,yes,1)告诉服务器端进入物理复制 Walsender 模式,其中可以发出如一小组复制命令,而不是 SQL 语句。如果 replication 参数的值设置为 replication=database,会指示服务器端进入逻辑复制 Walsender 模式,连接到 dbname 参数中指定的数据库。在逻辑复制 Walsender 模式中,可以发出复制命令以及普通的 SQL 命令。在物理复制或逻辑复制 Walsender 模式中,只能使用简单的查询协议。为了测试复制命令,可以使用带有包括 replication 选项的连接字符串的工具,通过 psql 或任何其他 libpq 驱动建立复制连接。例如,建立逻辑复制连接:1.Shell
2.psql "dbname=postgres replication=database" -c "IDENTIFY_SYSTEM;"
但是,使用 pg_receivewal(用于物理复制)或 pg_recvlogic(用于逻辑复制)工具通常更有用。如果连接中 log_replication_commands 启用时,复制命令会记录在服务器日志中。
在复制模式下可以接受的命令包括以下 8 种:IDENTIFY_SYSTEM、SHOW name、TIMELINE_HISTORY、CREATE_REPLICATION_SLOT、START_REPLICATION、START_REPLICATION LOGICAL、DROP_REPLICATION_SLOT、BASE_BACKUP。请求服务器标识自己。服务器使用单行的结果集进行回复,该结果集包含四个字段:
- dbname (text):连接的目标数据库。可以为 null。
name:运行时参数的名称。支持的参数名称此处不再赘述,参考服务器配置。请求服务器发送运行时参数的当前设置值。类似于 SQL 命令 SHOW。
请求服务器发送覆盖时间线 tli 的时间线历史文件。服务器使用单行的结果集进行回复,该结果集包含两个字段。字段被标记为 text,但它们有效地返回原始字节,无需编码转换:(4)CREATE_REPLICATION_SLOTCREATE_REPLICATION_SLOT slot_name [ TEMPORARY ] { PHYSICAL [ RESERVE_WAL ] | LOGICAL output_plugin [ EXPORT_SNAPSHOT | NOEXPORT_SNAPSHOT | USE_SNAPSHOT ] }
创建物理或逻辑复制插槽。关于复制插槽此处不再赘述,详细信息可以参考复制插槽。EXPORT_SNAPSHOT, NOEXPORT_SNAPSHOT, USE_SNAPSHOT:决定如何处理在逻辑插槽初始化期间创建的快照。EXPORT_SNAPSHOT 是默认值,它将导出快照以便在其他会话中使用。此选项不能在事务内部使用。USE_SNAPSHOT 将为执行该命令的当前事务使用快照。此选项必须在事务中使用,并且 CREATE_REPLICATION_SLOT 必须是该事务中运行的第一个命令。最后,NOEXPORT_SNAPSHOT 将正常使用快照进行逻辑解码,但不会对其执行任何其他操作。
作为对 CREATE_REPLICATION_SLOT 命令的响应,服务器将发送一个包含以下字段的单行结果集:完整命令为:
1.START_REPLICATION [ SLOT slot_name ] [ PHYSICAL ] XXX/XXX [ TIMELINE tli ]
指示服务器从 WAL 的位置 XXX/XX 开始流式传输 WAL。如果指定了 TIMELINE 选项,则流复制从时间线 tli 处开始;否则,将选择服务器的当前时间线。服务器可能会回复一个错误,例如,如果请求的 WAL 部分已经被回收。服务器处理成功后,将使用 CopyBothResponse 消息进行响应,然后开始将 WAL 流式传输到客户端。如果插槽的名称是通过 slot_name 提供的,则它将随着复制的进行而更新,以便服务器知道备机仍然需要哪些 WAL 段,以及 hot_standby_feedback 是否在事务上依然有效。如果客户端请求的时间线不是最新的,但是服务器历史的一部分,则服务器将流式传输该时间线上从请求的起始点开始直到服务器切换到另一个时间线为止的所有 WAL。如果客户端在旧时间线的末尾请求流式传输,则服务器将完全跳过 COPY 模式。在非最新时间线上流式传输所有 WAL 后,服务器将退出 COPY 模式以结束流式传输。当客户端通过退出 COPY 模式来确认这一点时,服务器会发送一个包含一行两列的结果集,指示该服务器历史中的下一个时间线。第一列是下一个时间线的 ID(类型为 int8),第二列是发生切换的 WAL 位置(类型为 text)。通常,切换位置是流式传输的 WAL 的末尾,但在某些情况下,服务器可以从旧的时间线发送一些 WAL,这些 WAL 是在时间线向前推进之前,服务器本身没有发送的 WAL。最后,服务器发送两条 CommandComplete 消息(一条结束 CopyData,另一条结束 START_REPLICATION 命令本身),并准备接收新命令。WAL 数据以一系列 CopyData 消息的形式发送。CopyData 消息之间允许混合其他信息。特别是,如果服务器在开始流式传输后遇到故障,则可以发送 ErrorResponse 消息。从服务器到客户端的每个 CopyData 消息的有效负载都包含以下格式之一的消息:→ Primary keepalive message (B)客户端接收进程可以随时使用以下消息格式之一(在 CopyData 消息的有效载荷中)将回复发送回 sender:→ Standby status update (F)→ Hot Standby feedback message (F)- Int32:备机的全局 xmin xid 的 epoch。
(6)START_REPLICATION LOGICAL1.START_REPLICATION SLOT slot_name LOGICAL XXX/XXX [ ( option_name [ option_value ] [, ...] ) ]
START_REPLICATION SLOT slot_name LOGICAL XXX/XXX [ ( option_name [ option_value ] [, ...] ) ]
指示服务器从 WAL 位置 XXX/XXX 开始流式传输 WAL 以进行逻辑复制。服务器可能会回复一个错误,例如,如果请求的 WAL 部分已经被回收。服务器处理成功后,将使用 CopyBothResponse 消息进行响应,然后开始将 WAL 流式传输到客户端。CopyBothResponse 消息中的消息与 START_REPLICATION ... PHYSICAL 文档中的格式相同,包括两条 CommandComplete 消息。与所选插槽关联的输出插件用于处理流式传输的输出。1.DROP_REPLICATION_SLOT slot_name [ WAIT ]
删除复制插槽,释放所有保留的服务器端资源。如果插槽是在 Walsender 连接的数据库之外的数据库中创建的逻辑插槽,则此命令将失败。
1.BASE_BACKUP [ LABEL 'label' ] [ PROGRESS ] [ FAST ] [ WAL ] [ NOWAIT ] [ MAX_RATE rate ] [ TABLESPACE_MAP ] [ NOVERIFY_CHECKSUMS ] [ MANIFEST manifest_option ] [ MANIFEST_CHECKSUMS checksum_algorithm ]
指示服务器开始流式传输基础备份。系统将在备份开始前自动进入备份模式,并在备份完成后退出。BASE_BACKUP 命令接受以下选项:
- TABLESPACE_MAP:在名为 tablespace_map 的文件中包含有关目录 pg_tblspc 中存在的符号链接的信息。表空间映射文件包括目录 pg_tblspc/ 中存在的每个符号链接名称以及该符号链接的完整路径。
启动备份时,服务器将首先发送两个普通结果集,然后发送一个或多个 CopyOutResponse 结果。第一个普通结果集为一行两列,包含备份的起始位置。第一列包含以 XLogRecPtr 格式给定的开始位置,第二列包含相应的时间线 ID。第二个普通结果集为每个表空间有一行。此行中的字段为:在第二个常规结果集之后,将发送一个或多个 CopyOutResponse 结果,一个用于主数据目录,其他的用于除 pg_default 和 pg_global 之外的每个附加表空间。CopyOutResponse 结果中的数据,除了省略了标准中规定的两个包含 0 值的块之外,是表空间内容的 tar 格式转储(遵循 POSIX 1003.1-2008 标准中指定的“ustar 交换格式”)。tar 数据完成后,如果请求了备份清单,则会发送另一个 CopyOutResponse 结果,其中包含当前基础备份的清单数据。在任何情况下,都会发送一个最终的普通结果集,其中包含备份的 WAL 结束位置,格式与开始位置相同。数据目录和每个表空间的 tar 存档将包含目录中的所有文件,无论它们是 PostgreSQL 文件还是添加到同一目录中的其他文件。只排除以下文件:- 跳过常规文件和目录以外的文件,如符号链接(上面列出的目录除外)和特殊设备文件。pg_tblspc 中的符号链接将得到保留维护。
如果服务器上的底层文件系统支持,则会设置所有者、组和文件模式。逻辑复制协议,是由 START_REPLICATION SLOT slot_name LOGICAL 复制命令启动的消息流。逻辑流复制协议建立在物理流复制协议的基础之上。逻辑复制 START_REPLICATION 命令接受以下参数:所有顶层协议消息都以消息类型字节作为开头。虽然在代码中表示为字符,但这是一个没有关联编码的带符号字节。由于流复制协议提供消息长度,因此顶层协议消息不需要在其标头中嵌入长度。每个逻辑复制消息要么由复制槽 SQL 接口返回,要么由 Walsender 发送。在 Walsender 的情况下,它们被封装在复制协议 WAL 消息中,并且通常遵循与物理复制相同的消息流。Begin
Message
Commit
Origin
注意:在一个事务中可以有多个 Origin 消息。
Relation
Byte1('R'):将消息标识为 Relation 消息。
Int32:事务的 Xid(仅用于流式事务)。此字段自协议版本 2 起可用。
Int32:Relation 的 ID。
String:命名空间(pg_catalog 命名空间为空字符串)。
String:Relation 名称。
Int8:Relation 的副本标识设置(与 pg_class 中的 relreplident 相同)。
Int16:列数量。
接下来,每个列(生成的列除外)都会显示以下消息部分:
Type
insert
Byte1('I'):将消息标识为插入消息。
Int32:事务的 Xid(仅用于流式事务)。此字段自协议版本 2 起可用。
Int32:与关系消息中的 ID 对应的关系的 ID。
Byte1('N'):将以下 TupleData 消息标识为新元组。
TupleData:表示新元组内容的 TupleData 消息部分。
update
Byte1('U'):将消息标识为更新消息。
Int32:事务的 Xid(仅用于流式事务)。此字段自协议版本 2 起可用。
Int32:与关系消息中的 ID 对应的关系的 ID。
Byte1('K'):将以下 TupleData 子消息标识为键。此字段是可选的,仅当更新操作更改了 REPLICA IDENTITY 索引中任何列中的数据时才出现。
Byte1('O'):将以下 TupleData 子消息标识为旧元组。此字段是可选的,仅当发生更新的表的 REPLICA IDENTITY 设置为 FULL 时才存在。
TupleData:表示旧元组或主键内容的 TupleData 消息部分。仅当存在前一个 O 或 K 部分时才存在。
Byte1('N'):将以下 TupleData 消息标识为新元组。
TupleData:表示新元组内容的 TupleData 消息部分。
更新消息可能包含 K 消息部分或 O 消息部分,也可能两者都不包含,但决不能同时包含两者。
delete
Byte1('D'):将消息标识为删除消息。
Int32:事务的 Xid(仅用于流式事务)。此字段自协议版本 2 起可用。
Int32:与关系消息中的 ID 对应的关系的 ID。
Byte1('K'):将以下 TupleData 子消息标识为键。如果发生删除的表使用了 REPLICA IDENTITY 的索引,则会出现此字段。
Byte1('O'):将以下 TupleData 消息标识为旧元组。如果发生删除的表的 REPLICA IDENTITY 设置为 FULL,则会出现此字段。
TupleData:TupleData 消息部分表示旧元组或主键的内容,具体取决于前一个字段。
delete 消息可能包含 K 消息部分或 O 消息部分,但决不能同时包含这两个部分。
truncate
Byte1('T'):将消息标识为 truncate 消息。
Int32:事务的 Xid(仅用于流式事务)。此字段自协议版本 2 起可用。
Int32:关系的数量。
Int8:truncate 的选项位:1 用于 CASCADE,2 用于 RESTART IDENTITY。
Int32:与关系消息中的 ID 对应的关系的 ID。对于每个关系,都会重复此字段。
自协议版本 2 以来,以下消息(流启动、流停止、流提交和流中止)可用。
11. Stream Start
11. Stream Stop
14. 以下消息部分由上述消息共享:
TupleData,Int16:列数。接下来,每列(生成的列除外)都会显示以下子消息之一:
Byte1('n'):将数据标识为 NULL 值。
Byte1('u'):标识未更改的 TOASTed 值(不发送实际值)。
Byte1('t'):将数据标识为文本格式的值。
Byte1('b'):将数据标识为二进制格式的值。
Int32:列值的长度。
Byten:列的值,可以是二进制格式,也可以是文本格式。(如前面格式字节中所指定的)。n 是上述列值长度。