技术贴 | 深度解析 PostgreSQL Protocol v3.0(三)— 流复制(下)

文摘   科技   2024-08-06 18:20   上海  

“PostgreSQL Protocol v3.0 深度解析系列博客,主要围绕 PostgreSQL  Protocol 3.0 版本具体实现和大家做分享,相关的代码解读基于 PostgreSQL 代码仓库的 REL_14_STABLE 分支。


往期系列🔻
技术贴 | 深度解析 PostgreSQL Protocol v3.0(一)
技术贴 | 深度解析 PostgreSQL Protocol v3.0(二)— 扩展查询
技术贴 | 深度解析 PostgreSQL Protocol v3.0(三)— 流复制(上)

上篇博客中,我们重点介绍了流复制概念、复制命令及逻辑流复制协议,本期将重点介绍扩展查询及消息格式、流复制交互流程。


01

扩展查询涉及消息的格式



1. Query(F:'Q'),Query 消息格式如下:


○ Byte1('Q')
将消息标识为简单查询。
○ Int32
消息内容的字节长度,包括自身 4 个字节。
○ String
查询字符串。

2. CopyInResponse(B:'G'),CopyInResponse 消息格式如下:


○ Byte1('G')
将消息标识为 Start Copy In 响应。客户端现在必须发送复制数据。如果客户端不准备这样做,需要发送 CopyFail 消息。
○ Int32
消息内容的字节长度,包括自身 4 个字节。
○ Int8
0:表示整个 COPY 格式是文本格式(行用换行符分隔,列用分隔符分隔,等等)。1:表示整个 COPY 格式是二进制的,类似于 DataRow 格式。
○ Int16
要复制的数据中的列数,在下面表示为 N。
○ Int16[N]
用于每列的格式代码。每个值必须为 0(文本)或 1(二进制)。如果整个 COPY 格式是文本格式,则所有值必须为 0。

3. CopyOutResponse(B:'H'),CopyOutResponse 消息格式如下:


○ Byte1 ('H')
将消息标识为 Start Copy Out 响应,后跟复制数据。
○ Int32
消息内容的字节长度,包括自身 4 个字节。
○ Int8
0:表示整个 COPY 格式是文本格式(行用换行符分隔,列用分隔符分隔,等等)。1:表示整个 COPY 格式是二进制的,类似于 DataRow 格式。
○ Int16
要复制的数据中的列数,在下面表示为 N。
○ Int16 [N]
用于每列的格式代码。每个值必须为 0(文本)或 1(二进制)。如果整个 COPY 格式是文本格式,则所有值必须为 0。

4. CopyBothResponse(B:'W'),CopyBothResponse 消息格式如下:


○ Byte1 ('W')
将消息标识为 Start Copy Both 响应。此消息仅用于流式复制。
○ Int32
消息内容的字节长度,包括自身 4 个字节。
○ Int8
0:表示整个 COPY 格式是文本格式(行用换行符分隔,列用分隔符分隔,等等)。1:表示整个 COPY 格式是二进制的,类似于 DataRow 格式。
○ Int16
要复制的数据中的列数,在下面表示为 N。
○ Int16 [N]
用于每列的格式代码。每个值必须为 0(文本)或 1(二进制)。如果整个 COPY 格式是文本格式,则所有值必须为 0。

5. CopyData(F&B:'d'),CopyData 消息格式如下:


○ Byte1('d')
将消息标识为 COPY 数据。
○ Int32
消息内容的字节长度,包括自身 4 个字节。
○ Byten
构成 COPY 数据流的一部分数据。从服务端发送的消息总是对应于单个数据行,但客户端发送的消息可能会任意划分数据流。

6. CopyDone(F&B:'c'),CopyDone 消息格式如下:


○ Byte1('c')
将消息标识为 COPY 完成指示符。
○ Int32(4)
消息内容的字节长度,包括自身 4 个字节。值总是为 4。

7. CopyFail(F:'f'),CopyFail 消息格式如下:


○ Byte1('f')
将消息标识为 COPY 失败指示符。
○ Int32
消息内容的字节长度,包括自身 4 个字节。
○ String
要报告失败原因的错误消息。

8. RowDescription(B:'T'),RowDescription 消息格式如下:


○ Byte1 ('T')
将消息标识为行描述。
○ Int32
消息内容的字节长度,包括自身 4 个字节。
○ Int16
指定一行中的字段数(可以为 0)。
对于行描述中的每个字段,都有以下 7 部分内容:
○ String
字段名称
○ Int32
如果字段可以被标识为特定表的列,则值为该表的对象 ID;否则为 0。
○ Int16
如果该字段可以标识为特定表的列,则值为该列的属性编号;否则为 0。
○ Int32
字段数据类型的对象 ID。
○ Int16
数据类型大小(可以参考 pg_type.typlen)。需要注意的是,负值表示可变宽度类型。
○ Int32
类型修饰符(可以参考 pg_attribute.atttypmod)。修饰符的含义是特定于数据类型的。
○ Int16
字段的格式代码。目前,只能是 0(文本)或 1(二进制)。在从 Describe 语句请求返回的 RowDescription 中,格式代码还未知,并且始终为零。

蓝色背景的部分,是每个列的详细描述。浅灰色背景的部分是可选的,格式是蓝色部分的重复。

9. DataRow(B:'D'),DataRow 消息格式如下:


○ Byte1 ('D')
将消息标识为数据行。
○ Int32
消息内容的字节长度,包括自身 4 个字节。
○ Int16
后续列值的数量(可能为 0)。
接下来,为每列的值显示以下一对字段:
○ Int32
列值的长度,以字节为单位(此计数不包括长度字段自身的 4 个字节)。该字段值可以为零。作为一种特殊情况,-1 表示列值为 NULL。在列值为 NULL 的情况下,后面没有值字节的字段。
○ Byten
列的值,格式由关联的格式代码指示。n 是上述字段的长度值。

蓝色背景的部分,是每个列值的字节长度和列值,是可选的。浅灰色背景的部分是可选的,格式是蓝色部分的重复。

10. CommandComplete(B:'C'),CommandComplete 消息格式如下:


○ Byte1 ('C')
将消息标识为命令完成。
○ Int32
消息内容的字节长度,包括自身 4 个字节。
○ String
命令标记。这通常是一个单词,用于标识完成了哪个 SQL 命令。

  • 对于 INSERT 命令,标记是 INSERT oid rows,其中 rows 是插入的行数。如果 rows 为 1 并且目标表具有 oid,则 oid 表示插入行的对象 ID。但 OIDs 系统列不再受支持;因此 oid 总是 0。
  • 对于 DELETE 命令,标记为 DELETE rows,其中 rows 是删除的行数。
  • 对于 UPDATE 命令,标记是 UPDATE rows,其中 rows 是更新的行数。
  • 对于 SELECT 或 CREATE TABLE AS 命令,标记是 SELECT rows,其中 rows 是检索的行数。
  • 对于 MOVE 命令,标记为 MOVE rows,其中 rows 是游标位置已更改的行数。
  • 对于 FETCH 命令,标记是 FETCH rows,其中 rows 是从游标检索的行数。
  • 对于 COPY 命令,标记是 COPY rows,其中 rows 是复制的行数。(注意:行计数仅出现在 PostgreSQL 8.2 及更高版本中。)

11. Flush(F:'H'),Flush 消息格式如下:


○ Byte1('H')
将消息标识为 Flush 消息。
○ Int32(4)
消息内容的字节长度,包括自身 4 个字节。始终为 4。

12. Sync(F:'S'),Sync 消息格式如下:



○ Byte1 ('S') 
将消息标识为 Sync 消息。
○ Int32 (4) 
消息内容的字节长度,包括自身 4 个字节。值始终为 4。

13. ErrorResponse(B:'E'),ErrorResponse 消息格式如下:


○ Byte1 ('E') 
将消息类型标识为错误。
○ Int32 
消息内容的字节长度,包括自身 4 个字节。
○ 消息体
消息体由一个或多个标识字段组成,后跟一个 '\0' 字节作为终止符。字段可以按任何顺序出现。对于每个字段,都有以下内容:

  • Byte1 标识字段类型的代码。如果为零,则这是消息终止符,后面没有字符串。由于将来可能会添加更多的字段类型,客户端应默默忽略无法识别类型的字段。ErrorResponse 消息体可能出现的字段标识及其描述可以参考《PostgreSQL Protocol v3.0 深度解析之概览》的 "5.错误消息和通知消息" 章节。
  • String 字段值。蓝色背景的部分,是消息体的一个字段类型及其值以及结束符 '\0'。一个 ErrorResponse 消息至少包含一个消息体,浅灰色背景的部分是可选的,格式是蓝色部分的重复。消息体的最后,有一个 '\0' 作为消息的结束。

14. ReadyForQuery(B:'Z'),ReadyForQuery 消息格式如下:


○ Byte1 ('Z')
将消息标识为服务器端准备好接收客户端的查询请求类型。每当服务器为新的查询周期做好准备时,就会发送 ReadyForQuery。
Int32 (5) 
消息内容的字节长度,包括自身 4 个字节。值总是 5。
Byte1 当前服务器端事务状态指示器。可能的值为:'I':处于空闲状态(不在事务块中); 'T':在事务块中; 'E':在失败的事务块中(查询将被拒绝,直到事务块结束)。

15. ParameterStatus(B:'S'),ParameterStatus 消息格式如下:


○ Byte1('S')
将消息标识为运行时参数状态报告。
○ Int32
消息内容的字节长度,包括自身 4 个字节。
○ String
要报告的运行时参数的名称。
○ String
参数的当前值。

16. NoticeResponse(B:'N'),NoticeResponse 与 ErrorResponse 消息格式以及消息体的内容完全一致。消息格式如下:


○ Byte1('N')
将消息标识为通知。
○ Int32
消息内容的字节长度,包括自身 4 个字节。
○ 消息体
消息体由一个或多个标识字段组成,后跟一个'\0'字节作为终止符。字段可以按任何顺序出现。对于每个字段,都有以下内容:

  • Byte1 标识字段类型的代码。如果为零,则这是消息终止符,后面没有字符串。由于将来可能会添加更多的字段类型,客户端应默默忽略无法识别类型的字段。NoticeResponse 消息体可能出现的字段标识及其描述可以参考《PostgreSQL Protocol v3.0 深度解析之概览》的"5.错误消息和通知消息"章节。
  • String,字段值。蓝色背景的部分,是消息体的一个字段类型及其值以及结束符'\0'。一个 NoticeResponse 消息至少包含一个消息体,浅灰色背景的部分是可选的,格式是蓝色部分的重复。消息体的最后,有一个'\0'作为消息的结束。



02

流复制交互流程



流复制交互流程,主要包括 COPY-IN、COPY-OUT 和 COPY-BOTH 三种模式,COPY-BOTH 模式又分为物理复制模式和逻辑复制模式。下面将对集中模式的交互流程分别进行介绍。

4.1 COPY-IN 交互流程

1. 客户端发起包含 COPY FROM STDIN 的 Query 查询消息。
2. 服务器端接收到 COPY FROM STDIN 查询消息,将连接切换到 COPY-IN 模式。切换模式成功后,向客户端发送 CopyInResponse 消息。
3. 客户端向服务器端发送数据消息 CopyData,可能为一个或多个,数据发送完成之后,发送 CopyDone 消息。
4. 服务器端接收到 CopyDone 消息,结束 COPY-IN 模式,恢复到 COPY-IN 模式之前的 SQL 命令处理流程。然后向客户端发送 CommandComplete 消息和 ReadyForQuery 消息。

具体的交互消息和交互流程,如下图所示:


4.2 COPY-OUT 交互流程

1. 客户端发起包含 COPY TO STDOUT 的 Query 查询消息。
2. 服务器端接收到 COPY TO STDOUT 查询消息,将连接切换到 COPY-OUT 模式。切换模式成功后,向客户端发送 CopyOutResponse 消息。
3. 紧接着,服务器端向客户端发送数据消息 CopyData,可能为一个或多个,数据发送完成之后,发送 CopyDone 消息。
4. 服务器端发送 CopyDone 消息后,结束 COPY-OUT 模式,恢复到 COPY-OUT 模式之前的 SQL 命令处理流程。然后向客户端发送 CommandComplete 消息和 ReadyForQuery 消息。

具体的交互消息和交互流程,如下图所示:


4.3 COPY-BOTH 交互流程
COPY-BOTH 模式主要包括物理复制模式、逻辑复制模式和 BASE_BACKUP 模式,三种模式在交互流程上略有不同。下面分别对物理复制模式、逻辑复制模式和 BASE_BACKUP 模式的交互流程进行详细介绍。

→ COPY-BOTH 物理复制模式

1. 客户端发起连接,向服务器端发送 StartupMessage 消息,包含参数 replication,值设置为 true,或 on,或 yes,或 1。

2. 服务器端接收到连接启动消息和 replication 参数,进入物理复制 WAL 发送者模式。

3. 可选,客户端发送包含 IDENTIFY_SYSTEM 查询字符串的 Query 消息。服务器端返回 DataRow 消息和 CommandComplete 消息。

4. 可选,客户端发送包含 SHOW name 查询字符串的 Query 消息。服务器端返回 DataRow 消息和 CommandComplete 消息。

5. 可选,客户端发送包含 TIMELINE_HISTORY tli 查询字符串的 Query 消息。服务器端返回 RowDescription、DataRow 消息和 CommandComplete 消息。

6. 可选,客户端发送包含 CREATE_REPLICATION_SLOT ... PHYSICAL ... 查询字符串的 Query 消息。服务器端返回 DataRow 消息和 CommandComplete 消息。

7. 客户端发送包含 START_REPLICATION_SLOT ... PHYSICAL ... 查询字符串的 Query 消息。服务器端返回 CopyBothResponseDataRow 消息、多条包含 XLogData 内容的 CopyData 消息,可选的包含 Primary keepalive message(B:'k') 内容的 CopyData 消息。

8. 可选,客户端可以向服务器端发送包含 Standby status update(F:'r') 内容的 CopyData 消息和 Hot Standby feedback message(F:'h') 内容的 CopyData 消息。

9. 服务端向客户端返回两条 CommandComplete 消息。

10. 可选,客户端发送包含 DROP_REPLICATION_SLOT ... 查询字符串的 Query 消息。服务器端返回 CommandComplete 消息。

具体的交互消息和交互流程,如下图所示:


COPY-BOTH 逻辑复制模式


1. 客户端发起连接,向服务器端发送 StartupMessage 消息,包含参数 replication,值设置为 database。

2. 服务器端接收到连接启动消息和 replication 参数,进入逻辑复制 WAL 发送者模式。

3. 可选,客户端发送包含 IDENTIFY_SYSTEM 查询字符串的 Query 消息。服务器端返回 DataRow 消息和 CommandComplete 消息。

4. 可选,客户端发送包含 SHOW name 查询字符串的 Query 消息。服务器端返回 DataRow 消息和 CommandComplete 消息。

5. 可选,客户端发送包含 TIMELINE_HISTORY tli 查询字符串的 Query 消息。服务器端返回 RowDescription、DataRow 消息和 CommandComplete 消息。

6. 客户端发送包含 START_REPLICATION_SLOT ... LOGICAL ... 查询字符串的 Query 消息。服务器端返回 CopyBothResponseDataRow 消息、多条包含 XLogData 内容的 CopyData 消息,可选的包含 Primary keepalive message(B:'k') 内容的 CopyData 消息。

7. 可选,客户端可以向服务器端发送包含 Standby status update(F:'r') 内容的 CopyData 消息和 Hot Standby feedback message(F:'h') 内容的 CopyData 消息。

8. 服务端向客户端返回两条 CommandComplete 消息。

9. 可选,客户端发送包含 DROP_REPLICATION_SLOT ... 查询字符串的 Query 消息。服务器端返回 CommandComplete 消息。

具体的交互消息和交互流程,如下图所示:


COPY-BOTH 的 BASE_BACKUP 模式

1. 客户端发起连接,向服务器端发送 StartupMessage 消息,包含参数 replication。

2. 服务器端接收到连接启动消息和 replication 参数,进入复制模式。

3. 客户端发送包含 BASE_BACKUP ... 查询字符串的 Query 消息。服务器端返回 RowDescription、DataRow 消息和 CommandComplete 消息,记录起始位置。

4. 服务器端发送一条 RowDescription 消息,然后为每个 tablespace 发送一条 DataRow 消息,后跟 CommandComplete 消息和 CopyOutResponse 消息。

5. 服务器端然后开始复制数据,为每个 tablespace 发送多个 CopyData 消息和一个 CopyDone 消息。所有 tablespace 数据发送完成后,发送 CommandComplete 消息和 CopyOutResponse 消息。

6. 服务器端最后发送 RowDescription、DataRow 消息和 CommandComplete 消息,记录复制结束位置。

具体的交互消息和交互流程,如下图所示:



以上就是 PostgreSQL Protocol v3 协议中流复制协议的全部内容介绍,主要应用于数据复制和主备场景,往期推荐可回看往期博客详情。欢迎大家关注、点赞、围观 😬







往期推荐


深度解析 PostgreSQL Protocol v3.0(一)
深度解析 PostgreSQL Protocol v3.0(二)— 扩展查询
技术贴 | 深度解析 PostgreSQL Protocol v3.0(三)— 流复制(上)



点击了解“关于 KaiwuDB”!

KaiwuDB
KaiwuDB 是浪潮控股的数据库企业,面向工业物联网、数字能源、车联网、智慧产业等各大行业领域,提供稳定安全、高性能、易运维的创新数据软件与服务,一站式满足 AIoT 等场景下数据管理需求及关键行业核心系统的自主可控需求
 最新文章