受支持版本: 当前版本 (18) / 17 / 16 / 15 / 14
开发版本: devel

Chapter 53. 前端/后端协议

PostgreSQL使用一种基于消息的协议在前端和后端(客户端与服务器)之间进行通信。该协议既支持TCP/IP,也支持 Unix 域套接字。端口号 5432 已在 IANA 注册为支持该协议的服务器的惯用 TCP 端口号,但实际上任何非特权端口号都可以使用。

本文描述协议 3.2 版本,该版本在 PostgreSQL 18 中引入。服务器和 libpq 客户端库向后兼容 PostgreSQL 7.4 及以后版本实现的协议 3.0。

为了高效地为多个客户端提供服务,服务器会为每个客户端启动一个新的后端进程。在当前实现中,一旦检测到传入连接,就会立刻创建新的子进程。不过,这一点对协议而言是透明的。就协议而言,术语后端服务器可以互换;同样,前端客户端也可以互换。

53.1. 概述 #

协议在启动和正常操作过程中有不同的阶段。在启动阶段里,前端打开一个到服务器的连接并且认证自身以满足服务器(这可能涉及到一条或多条消息,取决于使用的认证方法)。 如果一切正常,服务器就发送状态信息给前端,并最后进入正常操作。除了最初的启动请求消息之外,协议的这个部分是服务器驱动的。

在正常操作中,前端发送查询和其它命令到后端,然后后端返回查询结果和其它响应。在少数几种情况(比如NOTIFY)中,后端会发送未被请求的消息,但这个会话中的绝大多部分都是由前端请求驱动的。

会话的终止通常是由前端来选择的,但是也可以在某些情况下由后端强制执行。不管在那种情况下,如果后端关闭连接,那么它将在退出之前回滚所有打开的(未完成的)事务。

在正常操作中,SQL 命令可以通过两种子协议之一执行。在简单查询协议中,前端只需发送文本形式的查询字符串,后端会立即对其进行解析并执行。在扩展查询协议中,查询处理被拆分为多个步骤:解析、参数值绑定以及执行。这带来了更高的灵活性和性能收益,但代价是额外的复杂性。

正常操作还包含用于COPY等特殊操作的额外子协议。

53.1.1. 消息概述 #

所有通信都通过消息流进行。消息的第一个字节标识消息类型,接下来的四个字节给出消息其余部分的长度(该长度计数包含自身,但不包括消息类型字节)。消息剩余内容由消息类型决定。由于历史原因,客户端发送的第一条消息(启动消息)没有开头的消息类型字节。

为了避免与消息流失去同步,服务器和客户端通常都会先根据字节计数把整条消息读入缓冲区,然后再处理其内容。这样一来,如果在处理内容时检测到错误,就比较容易恢复。在极端情况下(例如没有足够内存缓冲整条消息),接收方也可以利用字节计数判断在恢复读取消息之前需要跳过多少输入。

反过来,服务器和客户端也必须注意绝不能发送不完整的消息。通常的做法是在开始发送之前,先在缓冲区中整理好整条消息。如果在发送或接收消息的途中发生通信故障,唯一合理的做法就是放弃连接,因为几乎不可能重新恢复消息边界的同步。

53.1.2. 扩展查询概述 #

在扩展查询协议中,SQL 命令的执行被拆分为多个步骤。各步骤之间保留的状态由两类对象表示:预备语句portal。预备语句表示对文本查询字符串完成解析和语义分析后的结果。预备语句本身还不能直接执行,因为它可能缺少特定的参数值。portal 表示一条已经可以执行、或已经部分执行过的语句,其中所有缺失的参数值都已补齐。(对于SELECT语句,portal 等价于一个打开的游标;但由于游标不能处理非SELECT语句,这里采用不同术语。)

整个执行周期包括一个解析步骤,它从文本查询字符串创建预备语句; 一个绑定步骤,它根据预备语句和所需参数值创建 portal; 以及一个执行步骤,用于执行 portal 中的查询。对于返回行的查询 (SELECTSHOW等),可以要求执行步骤只取回 有限数量的行,因此可能需要多次执行步骤才能完成整个操作。

后端可以跟踪多个预备语句和 portal(但请注意,它们只存在于单个会话内,绝不会在会话之间共享)。已有的预备语句和 portal 都通过创建时赋予的名称来引用。此外,还存在一个未命名的预备语句和 portal。虽然它们的行为与有名对象大体相同,但对未命名对象的操作是为“只执行一次然后丢弃”的场景优化的,而对有名对象的操作则是基于会被多次使用的预期进行优化的。

53.1.3. 格式和格式代码 #

某一特定数据类型的数据可以使用多种不同的格式之一进行传输。自 PostgreSQL 7.4 起,当前只支持文本二进制两种格式,但协议为未来扩展留出了空间。任意值所需的格式由格式代码指定。客户端可以为每个传输的参数值以及查询结果的每一列指定格式代码。文本格式的代码为零,二进制格式的代码为一,其他格式代码则保留供将来定义。

文本形式的数值是特定数据类型的输入/输出转换函数生成或接受的任何字符串。在传输形式上,字符串没有末尾空字符;如果前端要想把收到的值当作C字符串处理,那么必须自己加上一个(顺便说一下,文本格式不允许嵌入空字符)。

整数的二进制表示采用网络字节序(最高有效字节在前)。至于其他数据类型,请查阅文档或源代码了解其二进制表示形式。要注意,复杂数据类型的二进制表示可能会在不同服务器版本之间发生变化;文本格式通常是可移植性更好的选择。

53.1.4. 协议版本 #

当前最新协议版本为 3.2。不过,为了兼容尚不支持版本协商的旧服务器和中间件,libpq 默认仍使用 3.0。

单个服务器可以支持多个协议版本。初始启动请求消息会告诉服务器客户端尝试使用的协议版本。如果客户端请求的主版本服务器不支持,则连接会被拒绝(例如客户端请求 4.0,而截至本文编写时并不存在该版本)。如果客户端请求的次版本服务器不支持(例如客户端请求 3.2,但服务器只支持 3.0),服务器可以拒绝连接,也可以返回 NegotiateProtocolVersion 消息并给出其支持的最高次版本。客户端随后可以选择按该版本继续连接,或中止连接。

协议版本协商在 PostgreSQL 9.3.21 中引入。更早版本在客户端请求不支持的次版本时会直接拒绝连接。

Table 53.1 给出了当前支持的协议版本。

Table 53.1. 协议版本

版本 支持范围 说明
3.2 PostgreSQL 18 及以后 当前最新版本。用于取消查询的 secret key 从 4 字节扩展为可变长度字段;BackendKeyData 与 CancelRequest 消息相应调整为可变长度负载。
3.1 - 保留。PostgreSQL 从未使用 3.1;之所以跳过该版本,是因为旧版 pgbouncer 在协议协商中存在缺陷,会错误宣称支持 3.1。
3.0 PostgreSQL 7.4 及以后  
2.0 至 PostgreSQL 13 详见旧版本 PostgreSQL 文档。

提交更正

如果您发现文档中有不正确的内容、与您使用特定功能的经验不符或需要进一步说明,请使用此表单来报告文档问题。