作者:辰宇
私有协议是什么?
PolarDB-X作为阿里巴巴自主研发的云原生分布式数据库,通过将数据拆分到多个基于MySQL发展而来的存储节点DN,每个存储节点DN承担合适的并发、数据存储和计算负载,计算节点处理分布式逻辑,最终得到一个具有稳定可靠、高度扩展性的分布式关系型数据库系统。计算节点和存储节点之间的通信协议,即集群内部通信协议,是系统的重要组成部分。
PolarDB-X的前身为TDDL中间件,通过在应用端上采用 Sharding 技术,应对高速增长的数据量,由于后端统一为 MySQL,中间的通信协议为传统 MySQL 的查询协议。1.0时代的 DRDS+RDS 的组合也延续了之前中间件的技术路线,同样采用 MySQL 的 SQL 查询协议作为计算节点和存储节点的数据传输协议。
虽然 SQL 表示查询具有简洁易读,不用关心后端存储的物理实现等优点,但对于进入2.0时代(云原生分布式数据库)的PolarDB-X来说,计算节点和存储节点存在更多的数据交互,存储节点也不仅仅承担存储和处理普通SQL查询的功能,这时传统的SQL查询协议便显得力不从心了。
传统的SQL协议基于一问一答的阻塞式模型,是针对C/S模式的应用而设计的,没有考虑两个节点间承载大量会话的需要。而目前常见的分布式系统,特别是分布式数据库,为了保证事务性,节点间需要维持多条处于不同事物的会话。因此分布式数据库在集群内部往往都会实现一套自己的通信协议,通常都会选择RPC方式实现。例如OceanBase基于 Libeasy 库,实现了ObRPC,实现了集群内异步化通信。TiDB则直接使用了gRPC作为和TiKV的通信协议。为了充分发挥PolarDB-X底层存储的高级特性,同时解决传统SQL协议作为内部通信协议的局限性,私有协议应运而生。基于私有协议的加持,可以绕过传统的MySQL Server层的解析和优化开销,直接和底下的InnoDB/X-Engine存储引擎进行高效交互,同时扩展并支持了全局时钟协议交互、Plan计算下推等能力,这也使得PolarDB-X逐步脱离原有中间件的范畴,演变为云原生的分布式数据库。
PolarDB-X的私有协议作为计算节点和存储节点之间的桥梁,主要实现查询下发和结果回传等基础功能,同时针对一些常用的场景进行了特殊优化。私有协议不仅具备传统SQL接口执行SQL查询的功能,还能直接执行自定义的执行计划,使得计算节点和存储节点之间的交互更加灵活可控。同时为了提升网络交互性能,改善高并发多连接场景下稳定性,私有协议设计时也借鉴了RPC的处理思路,采用了异步化协议设计、会话连接解耦、实例级连接池、流量控制等机制,解决了大部分使用传统连接池遇到的问题,使得PolarDB-X的性能和稳定性都有所提升。
私有协议的设计和实现
兼容性 & 扩展性
聊到网络通信协议,首先需要考虑的就是协议兼容性。作为分布式系统,整个系统的变更都不可能是一步完成的,在升级或变配过程中,都可能出现多个不同版本的客户端或服务端。特别是在PolarDB-X中,存在计算节点和存储节点,在进行升级时,每个节点都可能存在升级前后两个版本,若通信协议发生了升级,则需要协议具备向前向后兼容的能力。
PolarDB-X私有协议在设计时充分考虑了这种情况,除了定长的协议头,所有通信协议消息均使用protobuf进行序列化,即使新增了额外的字段,旧版本的节点也能正常工作。考虑到系统的演进,私有协议支持基于SQL语句的查询,实现对SQL接口的完全替换,这部分扩展了MySQL官方的MySQL X protocol的实现,提供最大的兼容性。如下展示了PolarDB-X私有协议中SQL执行的message定义,在原有StmtExecute基础上,增加了一些额外字段,提升了会话上下文恢复速度,可避免通过额外SQL进行设置。
message StmtExecute {
optional string namespace = 3 [ default = "sql" ];
required bytes stmt = 1;
repeated Polarx.Datatypes.Any args = 2;
optional bool compact_metadata = 4 [ default = false ];
// Extended fields for fast context restore.
optional string schema_name = 5;
repeated Polarx.Datatypes.SessionVariable session_variables = 6;
optional string encoding = 7;
optional int32 token = 8;
optional bool reset_error = 9;
optional uint64 snapshot_seq = 10;
optional uint64 commit_seq = 11;
}
会话连接解耦 & 异步化 & 流量控制
在传统SQL接口中,一个TCP连接只能运行一个SQL会话,考虑到传统单机MySQL是作为一个服务端对外提供服务的,这个设计是非常合理的。但在PolarDB-X内部,MySQL被作为内部存储,在分布式系统中,存在多个计算节点和存储节点,在多连接复杂查询的场景下,后端会话数会因为数据分片的情况被成倍的放大,这时一个TCP连接对应一个SQL会话这种实现反而会带来局限性。因为TCP连接存在三层握手、内核TCP协议栈开销、连接鉴权等原因,使得动态扩容连接数是非常重的操作,而保留大量连接不仅耗费了大量系统资源同时增加了维护连接可靠的成本。
针对传统SQL接口连接池的局限性,PolarDB-X私有协议采用了会话连接解耦的策略,即采用在协议包头中添加SessionId字段,标记会话ID,实现在同一个TCP连接上并行运行多个SQL会话。
struct {
uint64 sid; // Session id. Default -1.
uint32 length;
uint8 message_type;
opaque message_payload[Message.length - 1];
} Message;
在PolarDB-X的存储节点上,使用一个独立的调度线程处理TCP上收到的消息,该线程负责会话的创建及生命周期管理,同时将解码后的消息放到对应的会话FIFO队列中,由MySQL的任务执行线程进行执行,指令执行完成后,由session所在TCP的socket返回执行结果。
基于PolarDB-X存储节点请求处理的FIFO特性,PolarDB-X计算节点使用异步调用和请求流水线的方式,最大化降低多指令的延迟。
多会话复用TCP连接,虽然降低了会话开销,但带来的额外的局限,最典型的问题则是多个session对于TCP通道的争抢。举个简单例子,一个TCP连接上,有一个点查和一个全表扫描的请求,计算节点全表扫描的消费速度没有发送快,导致该TCP通道被全表扫描的数据占据,点查结果无法及时发送过滤。针对这种场景,PolarDB-X私有协议设计了令牌机制,通过令牌对存储节点的生产者进行背压,而不是通过TCP的滑动窗口,从而保证短查询的时效性。同时在连接使用策略上,优先使用空闲连接,而不是优先复用,尽可能避免争抢的情况。
执行计划传输
SQL虽然能精简地描述一个查询,但这是需要额外使用解析器和优化器为代价的,在PolarDB-X中,计算节点和存储节点之间的数据传输指令也是用SQL实现的。但是在点查等简单的查询场景下,SQL的解析优化也成为制约存储节点吞吐量的一个瓶颈。PolarDB-X私有协议结合计算节点的特性,在优化阶段将下推执行的查询直接转换为特殊的执行计划。存储节点通过私有协议收到执行计划后,不用进行SQL解析和优化,直接进行数据的读取和处理。在实际测试中,同样吞吐量下,使用执行计划相对于使用SQL查询,存储节点的CPU的降低了50%,查询平均延迟也有近30%的下降。
高级查询特性支持
在OLAP任务中,常需要从存储节点拉取大量数据到计算节点进行处理,这对传输协议也是很大的挑战。PolarDB-X计算节点在内存中使用紧凑的列式数据存储结构,并借助向量化提升数据处理速度。PolarDB-X私有协议针对这种应用场景,通过自定义的传输格式的方式,可以直接以列式方式传输结果集,绕过传统传输协议行式传输带来的计算节点行转列的损失。同时针对列式数据格式的特性,后续也将具备压缩及压缩后计算的能力,进一步优化网络和内存的开销。
在复杂查询中,不仅传输的数据量大,同时复杂join也是非常常见的情况。通过添加runtime filter是一种有效的优化方法,但是在基于传统MySQL作为存储单元的情况下,这种高级过滤难以通过SQL进行表示。PolarDB-X中使用私有协议实现了bloom filter的传递,直接在存储节点上对数据进行过滤,极大减少了网络传输的压力。更多有用的高级查询特性,例如存储节点数据按规则主动分发、状态推送等也将逐步增加到PolarDB-X私有协议之中。
性能对比
使用私有协议后,最直接效果则是性能的提升,在点查场景下,私有协议相对于走SQL协议能极大降低后端存储的压力,同时提升吞吐量。
sysbench-select
- 1.6亿行数据
- 300并发
- 计算节点和存储节点规格均为16c64g
- +39%
sysbench-oltp
- 1.6亿行数据
- 150并发
- 计算节点和存储节点规格均为16c64g
- +14.4%
展望
PolarDB-X私有协议实现了对MySQL传统SQL查询协议的替换,并针对分布式数据库的特殊场景进行了扩展和改造。未来将在查询性能、网络开销和功能扩展上进一步优化和改造,提升PolarDB-X计算节点和存储节点的交互效率,为高性能PolarDB-X奠定坚实的基础。
参考文档
Core concepts, architecture and lifecycle