乐趣区

关于数据库:深度解析-PostgreSQL-Protocol-v30一

引言

PostgreSQL 应用基于音讯的协定在前端(也能够称为客户端)和后端(也能够称为服务器)之间进行通信。该协定通过 TCP/IP 和 Unix 域套接字反对。

《深度解析 PostgreSQL Protocol v3.0》系列技术贴,将带大家深度理解 PostgreSQL Protocol 3.0 版本(在 PostgreSQL 7.4 及更高版本中实现,无关晚期协定版本的形容请参考 PostgreSQL 文档的晚期版本,该系列文章不予赘述)相干的 音讯传输格局和格局码、音讯反对的数据类型、音讯的格局、协定交互流程、谬误音讯和告诉音讯、反对的子协定 等,相干的代码解读基于 PostgreSQL 代码仓库的 REL_14_STABLE 分支。

PostgreSQL 单个服务器能够反对多个协定版本,能够接管和解决多个不同版本协定的客户端的申请音讯。初始启动申请音讯通知服务器、客户端尝试应用的协定版本

  • 如果客户端申请的次要版本不受服务器反对,则连贯将被回绝(例如,如果客户端申请协定版本 4.0,而服务器端反对的协定版本不存在 4.0,此时就会产生这种状况);
  • 如果服务器不反对客户端申请的主要版本(例如,客户端申请版本为 3.1,但服务器仅反对 3.0,不反对 3.1 版本,此时就会产生这种状况),则服务器能够回绝连贯,或者能够应用蕴含其反对的最高主要协定版本的 NegotiateProtocolVersion 音讯进行响应。

客户端能够抉择应用服务器端指定的协定版本持续连贯或停止连贯。为了高效地为多个客户端提供服务,服务器为每个客户端启动一个新的过程进行申请解决。在以后实现中,在服务器检测到客户端的 Socket 连贯后立刻创立新的子过程进行后续的解决,比方 SSL 通信加密协商、启动音讯、身份认证等流程。

一、音讯传输的格局

客户端和服务器所有的交互都是通过音讯流进行的。每一条音讯次要由三局部组成:

  • 音讯类型
    用于标记音讯的类型,是单个字符或者 1 位的数字。音讯类型长度占用 1 个字节。
  • 音讯长度
    音讯中除了音讯类型之外的字节长度。音讯长度占用 4 个字节。音讯长度的值蕴含了音讯长度自身的 4 个字节长度。计算方法:
    (1)音讯字节总长度减去 1 字节的音讯类型的长度;
    (2)音讯内容字节总长度加上音讯长度自身占用的字节数 4。
  • 音讯体
    音讯的具体 payload 内容,例如简略查问的 SQL 内容。

须要留神的是,因为历史起因,客户端发送的第一条音讯(启动音讯)没有音讯类型的 1 个字节。服务器和客户端为了防止与音讯散失去同步,通常在尝试解决音讯内容之前将整个音讯读入缓冲区(应用字节计数)。

如果在解决音讯内容时检测到谬误,就能够轻松复原。在极其状况下(例如没有足够的内存来缓冲音讯),接收器能够应用字节计数来确定在复原读取音讯之前要跳过多少输出字节长度。服务器和客户端都必须留神不要发送不残缺的音讯。这通常是通过在开始发送之前在缓冲区中编码整个音讯来实现的。

如果在发送或接管音讯的过程中产生通信故障,那么惟一理智的做法是断开连接,因为复原音讯边界同步的心愿很小。

二、音讯反对的数据类型

PostgreSQL Protocol v3.0 的音讯中反对的数据类型只有以下 4 种:

  • Intn(i)
    n 位二进制示意的整数,为网络字节程序(最高无效字节优先,MSB),n 示意该值占用的位数。

如果指定了 i,则 i 是将呈现的确切值;如果未指定 i 值,该值是可变的。例如,Int16 示意一个值未指定的占用 16 位二进制位的整数(占用长度为 2 个字节,占用 16 位二进制);Int32(42)示意一个值为 42 的占用 32 位二进制位的整数(占用长度为 4 个字节,占用 32 位二进制)。

  • Intn[k]
    由 k 个 n 位二进制示意的整数组成的数组。数组长度 k 始终由音讯中较早的字段确定。
  • String(s)
    以空结尾的字符串(C-style 字符串)。字符串没有特定的长度限度。

如果指定了 s,则 s 是将呈现的确切值;如果未指定 s 值,该值是可变的。例如,String 示意一个值未指定的字符串;String(“user”)示意值为 user 的字符串。

须要留神的是,服务器能够返回的字符串长度没有预约义长度的限度,因而客户端比拟好的编码策略是应用可扩大缓冲区,以便能够接管适宜内存大小的内容。如果这不可行,请读取整个字符串并抛弃不适宜固定大小缓冲区的尾随字符。

  • Byten(c)

n 个字节。如果字段宽度 n 不是常数,则它总是能够从音讯中较早的字段确定。如果指定了 c,则 c 为该字段的准确值。例如,Byte2 示意值未指定的 2 个字节,Byte1(‘\n’)示意值为 ’\n’ 的 1 个字节。

除了以上四种数据类型,其余数据类型在 PostgreSQL Protocol v3.0 的音讯中均不反对。

三、音讯传输的格局和格局码

在 Postgresql Protocole 中,特定数据类型的数据能够用几种不同格局中的任何一种传输。

从 PostgreSQL 7.4(PostgreSQL Protocol v3.0)开始,协定反对的数据传输反对的格局是 text(文本)和 binary(二进制),该协定为未来的扩大做好了筹备。任何值传输的格局由格局代码指定。

客户端能够为每个传输的参数值和查问后果的每一列指定格局代码。text 的格局代码为 0,binary 的格局代码是 1,所有其余格局代码都保留以供未来定义。

值的 text 示意是输出 / 输入转换函数为特定数据类型生成 / 承受的字符串。在传输 text 的示意中,没有结尾空字符;如果客户端想要将接管到的值作为 C 格调字符串解决,则客户端必须自行将其加 1 个空字符。

须要留神的是,text 传输格局的值不容许内嵌空字符。整数的 binary 示意应用网络字节程序(最高无效字节优先,MSB)。

值得特地留神的是,简单数据类型的 binary 示意可能会在服务器版本之间发生变化;因而 text 格局通常是更便携更通用的抉择。

四、音讯的交互流程

PostgreSQL Protocol v3.0 的交互流程次要包含以下几种流程:

  • 1. 启动流程

要开始会话,客户端将关上与服务器的连贯并发送启动音讯 StartupMessage。启动音讯包含用户的名称、用户想要连贯到的数据库的名称和要应用的特定协定版本(启动音讯能够包含运行时参数的其余设置,然而这些参数都是可选的)。

接着,服务器应用这些信息及其配置文件(如 pg_hba.conf)的内容来确定连贯是否临时可承受,以及须要什么附加身份验证(如果有的话)。

而后,服务器发送适当的身份验证申请音讯,客户端必须用适当的身份认证响应音讯(如明码)回复该音讯。

对于除 GSSAPI、SSPI 和 SASL 之外的所有身份验证办法,最多只有一个申请和一个响应。在某些办法中,客户端不须要响应,因而不会产生身份验证申请。对于 GSSAPI、SSPI 和 SASL,可能须要屡次替换数据包能力实现身份验证。

  • 2. 简略查问流程

一个简略查问的周期由客户端端向服务器端发送查问音讯来启动。该音讯包含一个以文本字符串示意的 SQL 命令。而后,服务器依据查问命令字符串的内容进行执行,执行实现发送一条或多条响应音讯,最初发送 ReadyForQuery 响应音讯。

ReadyForQuery 告诉客户端,能够平安地发送新命令。(客户端实际上不须要在收回另一个命令之前期待 ReadyForQuery,但客户端必须负责弄清楚如果前一个命令失败,而曾经收回的后一个命令胜利,会产生什么状况。因而,倡议的做法是客户端接管到 ReadyForQuery 音讯之后再发送新命令。)

简略查问的交互流程中,也会呈现一些异常情况,会失去异样的响应。例如,查问 SQL 为空字符串,则响应为 EmptyQueryResponse,后跟 ReadyForQuery。产生谬误时,收回 ErrorResponse,而后收回 ReadyForQuery。ErrorResponse 会停止对查问字符串的所有进一步解决。

  • 3. 扩大查问

扩大查问协定将上述简略查问协定合成为多个步骤。为了提高效率,能够多次重复应用 Prepare 步骤的后果。

此外,还提供了其余性能,例如能够将数据值作为独自的参数提供,而不用将它们直接插入到查问字符串中。扩大查问个别须要通过 Parse, Bind 和 Execute 步骤,两头有一些可选步骤如 Describe,Close 和 Flush。

  • 4. Pipelining

扩大查问协定的应用容许流水线,这意味着发送一系列查问而无需期待较早的查问实现。流水线缩小了实现给定系列操作所需的网络往返次数。

然而,如果其中一个步骤失败,用户必须认真思考所需的解决,因为稍后的查问曾经在发送到服务器。

  • 5. 函数调用(Function Call)流程

函数调用 (Function Call) 子协定容许客户端申请间接调用数据库的 pg_proc 系统目录中存在的任何函数。客户端必须具备函数的执行权限。

函数调用子协定是一个较早版本的遗留性能,在新代码 / 新版本中最好防止应用。相似的后果能够通过设置执行 SELECT function($1, …)的筹备语句的值来实现。而后能够用 Bind/Execute 代替函数调用周期。

函数调用周期由客户端向端发送 FunctionCall 音讯来启动。服务端依据函数调用的后果发送一条或多条响应音讯,最初发送 ReadyForQuery 响应音讯。ReadyForQuery 告诉客户端它能够平安地发送新的查问或函数调用。

  • 6. 勾销执行中的申请流程

在解决查问期间,客户端可能会申请勾销查问。出于实现效率的起因,勾销申请不会间接通过正在执行查问的连贯发送到服务端:不心愿服务端在查询处理过程中一直查看来自客户端的新输出。勾销申请应该是绝对较少的,所以咱们让勾销流程略微麻烦一些,以防止在失常状况下产生谬误。

要收回勾销申请,客户端应该关上到服务器的新连贯并发送一条 CancelRequest 音讯,而不是通常通过新连贯发送的 StartupMessage 音讯。服务器将解决此申请,而后敞开连贯。出于平安起因,不间接回复勾销申请音讯。

  • 7. 完结流程

失常、敌对的终止过程是客户端发送 Terminate 终止音讯并立刻敞开连贯。服务端收到此 Terminate 音讯后,敞开连贯并终止。

在极少数状况下(如管理员通过命令敞开数据库),服务器端可能会在没有任何客户端申请的状况下断开连接。在这种状况下,服务器端将尝试在敞开连贯之前发送谬误或告诉音讯,给出断开连接的起因。

  • 8. COPY 操作

COPY 命令容许客户端与服务器之间进行高速批量数据传输。COPY IN 和 COPY OUT 操作都会将连贯切换到不同的子协定中,该子协定将继续到操作实现。

COPY IN 是将数据从客户端传输到服务器端,COPY OUT 是将数据从服务器端传输到客户端。还有另一种与 COPY 相干的模式,称为“双向复制”,它容许客户端与服务器之间的双向高速批量数据传输。

  • 9. 异步操作

有几种状况下,服务器端将向客户端发送客户端命令没有特地申请的音讯。客户端必须随时筹备好解决这些音讯,即便这些音讯不是为了响应查问申请。

因而,客户端在开始读取查问响应之前,至多应该查看这些状况。服务端异步发送给客户端的音讯次要有两种类型:NoticeResponse 音讯和 ParameterStatus 音讯。

五、谬误音讯和告诉音讯

谬误音讯 ErrorResponse 和告诉音讯 NoticeResponse,通常是在服务器端解决失败或者产生异样场景时,告诉客户端执行失败或者服务器端异样起因的音讯。

谬误音讯和告诉音讯中可能呈现的每个字段类型都有一个单字节标识,并且任何给定的字段类型在每条音讯中最多呈现一次。谬误音讯和告诉音讯中可能呈现的字段及其含意如下表所示。

客户端负责格式化显示谬误音讯和告诉音讯的信息以满足其须要。客户端应该依据须要进行换行等,谬误音讯字段中呈现的换行符应视为段落分隔符,而不是换行符。

六、其余子协定简介

1. 流复制协定(Streaming Replication Protocol)和逻辑流复制协定(Logical Streaming Replication Protocol)

要启动流复制,客户端在启动音讯中发送 replication 参数。replication 参数为布尔值,值为 true(或 on,yes,1)通知服务器端进入物理复制 walsender 模式,其中能够收回一组复制命令,而不是 SQL 语句。

将 database 作为 replication 参数的值传递,批示服务器端进入逻辑复制 walsender 模式,连贯到 dbname 参数中指定的数据库。在逻辑复制 walsender 模式下,能够收回复制命令以及失常的 SQL 命令。在物理复制或逻辑复制 walsender 模式中,只能应用简略的查问协定。

这两种协定,次要利用于主备服务器数据同步的场景。流复制也叫物理复制,是基于对文件块的流复制,逻辑复制是基于对数据元组依照肯定格局进行复制。

2. 加密协议

(1)SSL 会话加密

如果 PostgreSQL 的构建选项应用了 SSL,那么客户端和服务器端通信能够应用 SSL 加密。SSL 会话加密在攻击者可能可能捕捉会话流量的环境中提供了通信安全性。

(2)GSSAPI 会话加密

如果 PostgreSQL 的构建选项是应用了 GSSAPI,则能够应用 GSSAPI 加密客户端和服务器端的通信流量。这在攻击者可能可能捕捉会话流量的环境中提供了通信安全性。本篇技术贴中第四、五、六章节的相应内容,会在后续推出的《深度解析 PostgreSQL Protocol v3.0》系列文章中为大家进行具体开展,对本系列感兴趣的小伙伴欢送关注咱们,第一工夫获取更新内容噢。

退出移动版