PolarDB-X Glue 核心协议 (Core Protocol)
本文阐述了构成 PolarDB-X 粘合层 (PolarDB-X Glue) 与 PolarDB-X 引擎 (PolarDB-X Engine) 之间通信层的基本 Polarx 协议。它涵盖了支撑所有客户端-服务器端 (client-server) 交互的基础消息类型系统 (Message Type System) 、数据包帧结构 (Packet Framing Structure) 以及数据类型定义 (Data Type Definitions) 。
协议架构概览 (Protocol Architecture Overview)
核心协议 (Core Protocol) 建立了一个基于 Protobuf 的通信框架 (protobuf-based communication framework) ,具有三个基本组成部分:消息类型枚举 (message type enumeration) 、数据包帧 (packet framing) 和数据类型系统 (data type system) 。所有通信都通过此协议层使用结构化消息交换进行。

消息类型系统 (Message Type System)
协议定义了两种主要的消息类型枚举 (message type enumerations) ,用于确定客户端 (Client) 和服务器端 (Server) 之间有效的通信模式。

客户端消息类型 (Client Message Types)
ClientMessages.Type
枚举定义了所有可以从客户端发送到服务器端的消息,组织如下:
| 连接 (Connection) | CON_CAPABILITIES_GETCON_CAPABILITIES_SET, CON_CLOSE | |
| 会话 (Session) | SESS_AUTHENTICATE_STARTSESS_AUTHENTICATE_CONTINUE, SESS_RESET, SESS_CLOSE, SESS_NEW, SESS_KILL | |
| SQL 执行 (SQL Execution) | SQL_STMT_EXECUTEEXEC_SQL, GALAXY_PREPARE_EXECUTE | |
| CRUD 操作 (CRUD Operations) | CRUD_FINDCRUD_INSERT, CRUD_UPDATE, CRUD_DELETE | |
| 流量控制 (Flow Control) | TOKEN_OFFER | |
| 文件操作 (File Operations) | FILE_OPERATION_... |
服务器消息类型 (Server Message Types)
ServerMessages.Type
枚举定义了所有可以从服务器端发送到客户端的响应:
| 状态 (Status) | OKERROR | |
| 认证 (Authentication) | SESS_AUTHENTICATE_CONTINUESESS_AUTHENTICATE_OK | |
| 通知 (Notifications) | NOTICE | |
| 结果集 (Result Sets) | RESULTSET_COLUMN_META_DATARESULTSET_ROW, RESULTSET_FETCH_DONE, RESULTSET_FETCH_SUSPENDED, RESULTSET_FETCH_DONE_MORE_RESULTSETS, RESULTSET_FETCH_DONE_MORE_OUT_PARAMS | |
| SQL 响应 (SQL Responses) | SQL_STMT_EXECUTE_OK |
XPacket 结构 (XPacket Structure)
XPacket
类提供了基本的消息帧 (message framing) 机制,用于封装所有 Protobuf 消息以便进行网络传输。

关键字段 (Key Fields)
sid
: 会话标识符 (Session identifier) ,用于将数据包与特定的客户端会话关联。type
: 消息类型。packet
: 实际的 Protobuf 消息对象。packetSize
: 序列化 (serialized) Protobuf 消息的大小,用于网络优化。
预定义数据包 (Predefined Packets)
协议包含针对常见错误条件的预定义 XPacket
实例,如 EOF
(会话关闭)、KILLED
(会话被强制终止)和 FATAL
(致命会话错误)。
数据类型系统 (Data Type System)
PolarxDatatypes
包定义了用于协议消息中值编码的类型系统 (type system) ,提供了全面的标量类型系统 (scalar type system) 。

字符串和二进制类型 (String and Binary Types)
Scalar.String
: 文本值,带有可选的排序规则 (collation) 信息。Scalar.Octets
: 二进制数据,带有可选的内容类型元数据 (content type metadata) 。V_RAW_STRING
: 原始字符串编码 (Raw string encoding) ,用于性能关键场景。V_IDENTIFIER
: 数据库对象标识符 (identifiers) (如表名、列名)。
容器类型 (Container Types)
Any
: 包装类型 (Wrapper type) ,可以包含Scalar
(标量)或Array
(数组)值。Array
: 集合类型 (Collection type) ,用于多个Any
值。
类型转换系统 (Type Conversion System)
XUtil
类提供了 Java 对象和协议数据类型之间的转换层 (conversion layer) ,负责将 Java 值编码为具有正确类型信息和编码的适当 PolarxDatatypes.Scalar
实例。

关键转换特性 (Key Conversion Features)
编码感知字符串 (Encoding-aware strings) : 尊重会话编码设置。 原始字符串优化 (Raw string optimization) : 用于性能关键场景。 标识符处理 (Identifier handling) : 特殊处理 TableName
对象,将其转换为V_IDENTIFIER
标量。数组支持 (Array support) : 将 Java 集合转换为 PolarxDatatypes.Array
。Any 包装器 (Any wrapper) : 将 Scalar
或Array
值包装在Any
容器类型 (container type) 中。
类型映射表 (Type Mapping Table)
String | genStringScalar() | V_STRING | |
IntegerLong, Short... | genSIntScalar() | V_SINT | |
FloatDouble | genFloatScalar()genDoubleScalar() | V_FLOATV_DOUBLE | |
Boolean | genBooleanScalar() | V_BOOL | |
BigDecimalDecimal | genUtf8RawStringScalar() | V_RAW_STRING | |
byte[] | genOctetsScalar() | V_OCTETS | |
TableName | genIdentifierScalar() | V_IDENTIFIER | |
null | genNullScalar() | V_NULL |
附录1: 核心性能与架构选择 (Architect Performance Focus)
背景痛点: 数据库中间件的核心价值在于高性能和高吞吐。协议的序列化、反序列化和消息帧封装是主要的性能消耗点。
需要展开的细节: Protobuf、XPacket
帧结构、V_RAW_STRING
优化与传统协议的性能对比。
问题:
PolarDB-X 核心协议采用 Protobuf 作为序列化 (Serialization) 机制,并使用了自定义的XPacket
进行消息帧封装。相较于传统的 MySQL 协议或标准的 MySQL X Protocol,这种设计(特别是利用V_RAW_STRING
等优化)在高并发/大数据量传输场景下,其消息处理和类型转换的性能瓶颈主要在哪里?
架构师在设计应用时,如何利用协议层面的优化(如批量操作或特定数据类型编码)来最大化 Glue 层的吞吐量?
回答
核心性能瓶颈分析
PolarDB-X 核心协议在高并发/大数据量场景下的主要性能瓶颈集中在以下几个方面:
1. Protobuf 反序列化瓶颈
在 NIOClient
的数据包处理循环中,每个数据包都需要通过 switch 语句进行类型判断和相应的 Protobuf 反序列化 。这是主要的 CPU 消耗点:
每个数据包都需要调用 parseFrom()
方法进行反序列化大量的消息类型(OK、ERROR、RESULTSET_ROW 等)需要分支判断 反序列化过程涉及内存分配和对象创建
2. 缓冲区管理开销
网络层实现了动态缓冲区切换机制 :
正常数据包使用直接缓冲区(readBuffer) 大数据包自动切换到堆缓冲区(bigReadBuffer) 缓冲区切换涉及内存复制和 GC 压力
3. 流控令牌管理
XResult
实现了基于令牌的流控机制 :
每个数据包都会更新令牌计数 令牌不足时会触发网络等待 频繁的令牌计算和检查增加开销
协议层优化策略
1. V_RAW_STRING 优化
XPreparedStatement
中实现了 V_RAW_STRING
优化 :
if (x instanceof RawString) {
setParam(parameterIndex,
XUtil.genAny(XUtil.genUtf8RawStringScalar(((RawString) x).buildRawString(), connection.getSession())));
}
这避免了字符串的重复编码转换,直接传输原始字节数据。
2. Galaxy Prepare 批量优化
XSession
提供了 Galaxy Prepare 执行路径 :
使用 GALAXY_PREPARE_EXECUTE
消息类型支持批量参数绑定和执行 减少协议交互次数
3. 分块结果传输
协议支持分块结果传输(Chunk V1) :
if (chunkResult && supportChunkResult()) {
builder.setChunkResult(true);
}
这允许大数据集的分批传输,避免内存溢出。
架构师优化建议
1. 批量操作设计
使用 Galaxy Prepare 模式进行批量 SQL 执行 在 XPreparedStatement.addBatch()
中积累参数合理设置批次大小以平衡内存和网络开销
2. 数据类型优化
对于大文本数据,优先使用 RawString
类型避免编码转换利用 V_RAW_STRING
标量类型直接传输字节数据在字符集转换敏感的场景中显式指定编码
3. 流控调优
根据网络状况调整 defaultTokenKb
参数在高吞吐场景中适当增加令牌大小 监控令牌使用情况避免流控瓶颈
4. 连接池优化
利用实例级连接池减少连接建立开销 通过 XConnectionManager
进行全局资源协调合理配置连接池大小以匹配并发负载
Notes
V_RAW_STRING 优化主要适用于已知编码的大文本数据传输 Galaxy Prepare 模式不支持 V_RAW_STRING 参数类型 分块传输需要客户端和服务端同时支持 流控令牌机制是防止内存溢出的重要保护,不建议完全禁用
附录2: 开发与维护难度 (Developer Maintainability Focus)
背景痛点: 复杂的通信协议需要开发者投入大量精力进行联调和调试。若缺乏清晰的交互流程指引,很容易在客户端实现中引入时序或状态错误。
需要展开的细节: 客户端和服务端消息类型 (ClientMessages.Type
, ServerMessages.Type
) 的复杂交互,如 SQL 执行流程。
问题:
协议中定义了大量分类清晰的客户端/服务器端消息类型 (Message Type System) 。对于想要基于 Polarx 协议开发自定义客户端驱动或调试工具的开发者而言,如何在日常联调和维护中,高效地解析和追踪复杂的交互流程(例如,从SQL_STMT_EXECUTE
到RESULTSET_ROW
再到RESULTSET_FETCH_DONE
的完整生命周期)?
是否有公开的状态机或时序图来指导不同消息类别(如 Session, SQL Execution, Flow Control)的正确交互模式,以避免因协议顺序错误导致的痛点?
回答
基于 Polarx 协议的代码分析,我可以为你提供消息类型系统的交互流程追踪方法和状态机指导。
消息类型系统概览
Polarx 协议在 polarx.proto
中定义了完整的消息类型系统 :
客户端消息 ( ClientMessages.Type
):包含连接管理、会话管理、SQL 执行、流控制等类别服务端消息 ( ServerMessages.Type
):包含状态响应、结果集、通知等类别
SQL 执行流程状态机
完整的 SQL 执行生命周期

这个状态机在 XResult.java
中实现 ,定义了以下关键状态:
XResultStart
:等待元数据或影响行数XResultMeta
:接收列元数据XResultRows
:接收数据行XResultFetchDone
:结果集接收完成XResultFinish
:执行完成
消息交互时序图

消息类型分类和交互模式
1. Session 管理消息
SESS_NEW | OKERROR(1) | |
SESS_CLOSE | OK | |
SESS_KILL | ERROR |
2. SQL 执行消息
SQL_STMT_EXECUTE | ||
GALAXY_PREPARE_EXECUTE | ||
RESULTSET_COLUMN_META_DATA | ||
RESULTSET_ROW | ||
RESULTSET_FETCH_DONE | ||
SQL_STMT_EXECUTE_OK |
3. 流控制消息

流控制通过 TOKEN_OFFER
消息实现 ,客户端在缓冲区不足时主动请求更多令牌。
调试和追踪建议
1. 消息解析追踪
在 NIOClient.java
中可以看到完整的消息解析逻辑 :
switch (type) {
case Polarx.ServerMessages.Type.RESULTSET_COLUMN_META_DATA_VALUE:
// 处理列元数据
case Polarx.ServerMessages.Type.RESULTSET_ROW_VALUE:
// 处理数据行
case Polarx.ServerMessages.Type.RESULTSET_FETCH_DONE_VALUE:
// 处理结果集完成
}
2. 状态转换监控
XResult.java
中的状态机提供了完整的状态转换追踪 ,建议在调试时:
监控 status
字段的变化记录每个状态转换的触发消息类型 注意异常状态如 XResultFatal
的处理
3. 常见协议顺序错误
XResultMeta状态接收 RESULTSET_ROW | ||
XResultRows状态接收 RESULTSET_COLUMN_META_DATA | ||
RESULTSET_FETCH_DONE |
Notes
协议定义在 polarx.proto
和polarx_sql.proto
中,生成的 Java 代码在Polarx.java
和PolarxSql.java状态机实现主要在 XResult.java
中,涵盖了完整的结果处理生命周期网络层的消息解析在 NIOClient.java
中,提供了所有消息类型的处理逻辑会话管理和流控制在 XSession.java
中实现建议开发时参考这些文件中的状态转换逻辑,避免协议顺序错误
附录3: 用户与运维可靠性 (User / Reliability Focus)
背景痛点: 协议底层的错误(如连接断开、会话终止)需要清晰地映射到应用层的异常,才能让应用用户或运维人员做出正确判断(例如,是否可以安全重试)。
需要展开的细节: 预定义错误数据包 (EOF
, KILLED
, FATAL
) 的业务含义,以及流量控制 (TOKEN_OFFER
) 的影响。
问题:
协议中定义的EOF
、KILLED
和FATAL
这三类预定义错误数据包在实际生产环境中分别对应哪些常见的用户操作或运维背景痛点?
特别是KILLED
消息是否与流量控制 (Flow Control) 机制(如TOKEN_OFFER
)相关联?
当客户端收到这些低级别错误时,应用程序或运维人员应该采取哪些标准的重试或维护动作来确保数据一致性或快速恢复连接?
回答
预定义错误数据包的业务含义和应对策略
错误数据包定义和触发场景
EOF 错误数据包
表示会话正常关闭,通过 pushEOF()
方法触发,内部调用pushFatal(XPacket.EOF)常见于:应用正常关闭连接、会话超时后清理、连接池回收会话
KILLED 错误数据包
表示会话被强制终止,通过 pushKilled()
方法触发,内部调用pushFatal(XPacket.KILLED)常见于:管理员手动 KILL 会话、查询超时强制终止、连接异常断开
FATAL 错误数据包
表示不可恢复的致命错误,在协议定义中 severity 为 FATAL 时触发 常见于:网络连接彻底断开、数据库实例崩溃、协议版本不兼容
KILLED 与流量控制的关系
KILLED 消息不直接与流量控制(TOKEN_OFFER
)相关联,但存在间接关系:
流量控制通过 TOKEN_OFFER
消息实现,客户端主动发送令牌给服务器以控制数据传输速率当会话被 KILLED 时,会强制终止所有进行中的操作,包括流量控制机制 在 XConnection.close()
中,如果检测到未完成的请求,会调用session.kill(true)
来防止流量控制阻塞
标准应对策略
应用程序层面的处理
运维人员处理建议
EOF 错误:监控频率,如果过高可能需要调整连接池配置或会话超时时间 KILLED 错误:检查是否有长时间运行的查询,优化 SQL 性能,调整查询超时设置 FATAL 错误:立即检查数据库实例状态,网络连接,必要时重启应用服务
代码实现示例
在 XClient.setFatalError()
中,当发生致命错误时,会向所有工作会话推送 FATAL 消息并关闭连接 :
// 向所有会话推送致命错误
for (XSession session : workingSessionMap.values()) {
session.pushFatal(new XPacket(-1, Polarx.ServerMessages.Type.ERROR_VALUE,
Polarx.Error.newBuilder().setSeverity(Polarx.Error.Severity.FATAL)...));
}
Notes
流量控制机制主要在 XResult.internalFetchOneObject()
中实现,当缓冲区空间不足时会自动调用tokenOffer()错误处理逻辑在 XResult
中有详细实现,区分了 ERROR 和 FATAL 级别的错误会话状态管理通过状态机实现,包括 Init、Waiting、Ready、Closed 等状态




