暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

PolarDB-X Glue 核心协议 (Core Protocol)

PolarDB 2025-12-16
297

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
 枚举定义了所有可以从客户端发送到服务器端的消息,组织如下:

类别 (Category)
消息类型 (Message Types)
目的 (Purpose)
连接 (Connection)CON_CAPABILITIES_GET
, CON_CAPABILITIES_SET
, CON_CLOSE
连接生命周期管理
会话 (Session)SESS_AUTHENTICATE_START
, SESS_AUTHENTICATE_CONTINUE
, SESS_RESET
, SESS_CLOSE
, SESS_NEW
, SESS_KILL
会话认证和生命周期
SQL 执行 (SQL Execution)SQL_STMT_EXECUTE
, EXEC_SQL
, GALAXY_PREPARE_EXECUTE
SQL 语句执行
CRUD 操作 (CRUD Operations)CRUD_FIND
, CRUD_INSERT
, CRUD_UPDATE
, CRUD_DELETE
文档/表操作
流量控制 (Flow Control)TOKEN_OFFER
流量控制和背压 (backpressure)
文件操作 (File Operations)FILE_OPERATION_...
物理回填操作

服务器消息类型 (Server Message Types)

ServerMessages.Type
 枚举定义了所有可以从服务器端发送到客户端的响应:

类别 (Category)
消息类型 (Message Types)
目的 (Purpose)
状态 (Status)OK
, ERROR
操作完成状态
认证 (Authentication)SESS_AUTHENTICATE_CONTINUE
, SESS_AUTHENTICATE_OK
认证流程响应
通知 (Notifications)NOTICE
服务器到客户端通知
结果集 (Result Sets)RESULTSET_COLUMN_META_DATA
, RESULTSET_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
SQL 执行完成

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)

Java 类型 (Java Type)
XUtil 函数 (XUtil Function)
标量类型 (Scalar Type)
备注 (Notes)
String
genStringScalar()
V_STRING
编码感知
Integer
, Long
, Short
...
genSIntScalar()
V_SINT
有符号整数
Float
 / Double
genFloatScalar()
 / genDoubleScalar()
V_FLOAT
 / V_DOUBLE
单/双精度
Boolean
genBooleanScalar()
V_BOOL
布尔值
BigDecimal
, Decimal
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
(110)
OK
(0)/ERROR
(1)
创建新会话
SESS_CLOSE
(7)
OK
(0)
关闭会话
SESS_KILL
(111)
ERROR
(FATAL)
强制终止会话

2. SQL 执行消息

消息类型
方向
关键字段
SQL_STMT_EXECUTE
(12)
Client→Server
stmt, args, compact_metadata
GALAXY_PREPARE_EXECUTE
(102)
Client→Server
stmt, tables, param
RESULTSET_COLUMN_META_DATA
(12)
Server→Client
列元数据
RESULTSET_ROW
(13)
Server→Client
数据行
RESULTSET_FETCH_DONE
(14)
Server→Client
结果集结束
SQL_STMT_EXECUTE_OK
(17)
Server→Client
执行完成确认

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

  1. 协议定义在 polarx.proto
     和 polarx_sql.proto
     中,生成的 Java 代码在 Polarx.java
     和 PolarxSql.java
  2. 状态机实现主要在 XResult.java
     中,涵盖了完整的结果处理生命周期
  3. 网络层的消息解析在 NIOClient.java
     中,提供了所有消息类型的处理逻辑
  4. 会话管理和流控制在 XSession.java
     中实现
  5. 建议开发时参考这些文件中的状态转换逻辑,避免协议顺序错误

附录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
⚠️ 条件重试
检查事务状态,非事务操作可重试
需要回滚未提交事务
FATAL
❌ 不可重试
重建连接池,告警通知
可能存在数据丢失风险

运维人员处理建议

  1. EOF 错误:监控频率,如果过高可能需要调整连接池配置或会话超时时间
  2. KILLED 错误:检查是否有长时间运行的查询,优化 SQL 性能,调整查询超时设置
  3. 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 等状态

文章转载自PolarDB,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论