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 驱动建立复制连接。例如,建立逻辑复制连接:
但是,使用 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。
请求服务器标识自己。服务器使用单行的结果集进行回复,该结果集包含四个字段:
name:运行时参数的名称。支持的参数名称此处不再赘述,参考服务器配置。
请求服务器发送运行时参数的当前设置值。类似于 SQL 命令 SHOW。
完整命令为:
请求服务器发送覆盖时间线 tli 的时间线历史文件。服务器使用单行的结果集进行回复,该结果集包含两个字段。字段被标记为 text,但它们有效地返回原始字节,无需编码转换:
完整命令为:
创建物理或逻辑复制插槽。关于复制插槽此处不再赘述,详细信息可以参考复制插槽。 slot_name:要创建的插槽的名称。必须是有效的复制插槽名称,它可以包含小写字母、数字和下划线。
作为对 CREATE_REPLICATION_SLOT 命令的响应,服务器将发送一个包含以下字段的单行结果集:
slot_name (text):新创建的复制插槽的名称。
consistent_point (text):插槽变得一致的 WAL 位置。这是可以在此复制插槽上启动流的最早位置。
snapshot_name (text):命令导出的快照的标识符。在该连接上执行新命令或关闭复制连接之前,快照一直有效。如果创建的插槽是物理插槽,则为 null。
output_plugin (text):新创建的复制插槽使用的输出插件的名称。如果创建的插槽是物理插槽,则为 null。
完整命令为:
指示服务器从 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 消息的有效负载都包含以下格式之一的消息:
→ XLogData (B)
→ Primary keepalive message (B)
Byte1('k'):将消息标识为 sender keepalive。 Int64:服务器上 WAL 的当前结束。
Int64:服务器在传输时的系统时钟,自 2000-01-01 午夜起以微秒为单位。
Byte1:1,表示客户端应尽快回复此消息,以避免超时断开连接;否则为 0。
客户端接收进程可以随时使用以下消息格式之一(在 CopyData 消息的有效载荷中)将回复发送回 sender:
→ Standby status update (F)
→ Hot Standby feedback message (F)
Byte1('h'):将消息标识为热备反馈消息。
Int64:传输时客户端的系统时钟,自 2000-01-01 午夜起以微秒为单位。
Int32:备机的当前全局 xmin,不包括任何复制插槽中的 catalog_xmin。如果 xmin 和下面的 catalog_xmin 都为 0,则将被视为不再在此连接上发送热备反馈的通知。稍后的非 0 消息可以重新启动反馈机制。
Int32:备机的全局 xmin xid 的 epoch。
Int32:备机复制插槽中最低的 catalog_xmin。如果备机上不存在 catalog_xmin,或者热备反馈被禁用,则设置为 0。
Int32:备机的 catalog_xmin xid 的 epoch。
完整命令为:
START_REPLICATION SLOT slot_name LOGICAL XXX/XXX [ ( option_name [ option_value ] [, ...] ) ] 指示服务器从 WAL 位置 XXX/XXX 开始流式传输 WAL 以进行逻辑复制。服务器可能会回复一个错误,例如,如果请求的 WAL 部分已经被回收。服务器处理成功后,将使用 CopyBothResponse 消息进行响应,然后开始将 WAL 流式传输到客户端。
CopyBothResponse 消息中的消息与 START_REPLICATION ... PHYSICAL 文档中的格式相同,包括两条 CommandComplete 消息。与所选插槽关联的输出插件用于处理流式传输的输出。
完整命令为:
删除复制插槽,释放所有保留的服务器端资源。如果插槽是在 Walsender 连接的数据库之外的数据库中创建的逻辑插槽,则此命令将失败。 slot_name:要删除的插槽的名称。 WAIT:如果插槽处于活动状态,此选项会导致命令等待,直到它变为非活动状态,而不是引发错误的默认行为。
完整命令为:
指示服务器开始流式传输基础备份。系统将在备份开始前自动进入备份模式,并在备份完成后退出。BASE_BACKUP 命令接受以下选项:
启动备份时,服务器将首先发送两个普通结果集,然后发送一个或多个 CopyOutResponse 结果。
第一个普通结果集为一行两列,包含备份的起始位置。第一列包含以 XLogRecPtr 格式给定的开始位置,第二列包含相应的时间线 ID。
第二个普通结果集为每个表空间有一行。此行中的字段为:
在第二个常规结果集之后,将发送一个或多个 CopyOutResponse 结果,一个用于主数据目录,其他的用于除 pg_default 和 pg_global 之外的每个附加表空间。CopyOutResponse 结果中的数据,除了省略了标准中规定的两个包含 0 值的块之外,是表空间内容的 tar 格式转储(遵循 POSIX 1003.1-2008 标准中指定的“ustar 交换格式”)。
tar 数据完成后,如果请求了备份清单,则会发送另一个 CopyOutResponse 结果,其中包含当前基础备份的清单数据。在任何情况下,都会发送一个最终的普通结果集,其中包含备份的 WAL 结束位置,格式与开始位置相同。
数据目录和每个表空间的 tar 存档将包含目录中的所有文件,无论它们是 PostgreSQL 文件还是添加到同一目录中的其他文件。只排除以下文件:
如果服务器上的底层文件系统支持,则会设置所有者、组和文件模式。
逻辑复制协议,是由 START_REPLICATION SLOT slot_name LOGICAL 复制命令启动的消息流。逻辑流复制协议建立在物理流复制协议的基础之上。
逻辑复制 START_REPLICATION 命令接受以下参数:
所有顶层协议消息都以消息类型字节作为开头。虽然在代码中表示为字符,但这是一个没有关联编码的带符号字节。由于流复制协议提供消息长度,因此顶层协议消息不需要在其标头中嵌入长度。每个逻辑复制消息要么由复制槽 SQL 接口返回,要么由 Walsender 发送。在 Walsender 的情况下,它们被封装在复制协议 WAL 消息中,并且通常遵循与物理复制相同的消息流。
1.Begin
2.Message
3.Commit
4.Origin
5.Relation
接下来,每个列(生成的列除外)都会显示以下消息部分:
6.Type
7.Insert
8.Update
9.Delete
10.Truncate
11.Stream Start
Byte1('A'):将消息标识为流中止消息。
Int32:事务的 Xid。
Int32:子事务的 Xid(对于顶级事务,将和事务的 Xid 相同)。
12. Stream Stop
Byte1('E'):将消息标识为流停止消息。
Stream Commit
Byte1('c'):将消息标识为流提交消息。
Int32:事务的 Xid。
Int8:标识。当前未使用(必须为 0)。
Int64:提交的 LSN。
Int64:事务的结束 LSN。
Int64:事务的提交时间戳。该值以 PostgreSQL epoch(2000-01-01)以来的微秒为单位。
13.Stream Abort
Byte1('A'):将消息标识为流中止消息。
Int32:事务的 Xid。
Int8:值 1 表示这是该 XID 的第一个流段,0 表示任何其他流段。
14. 以下消息部分由上述消息共享:
TupleData,Int16:列数。接下来,每列(生成的列除外)都会显示以下子消息之一:
免费体验 KaiwuDB 全新功能
KaiwuDB
B站
KaiwuDB
微信公众号