2025-07-16
文件系统
0
请注意,本文编写于 32 天前,最后修改于 32 天前,其中某些信息可能已经过时。

目录

openGauss通信协议
一、消息格式
二、Extended Query 处理流程
2.1. Parse(解析):生成语法树
2.2. Bind(绑定):关联参数与执行计划
2.3. Execute(执行):执行查询并返回结果
2.4. Sync(同步):结束当前查询流程
2.5. 批量管线与 Sync

openGauss通信协议

 客户端可以通过两种 "子协议" 来发送请求,分别是 simple queryextened query。使用 simple query 时,客户端发送字符串文本请求,后端收到后立即处理并返回结果(gsql);使用 extened query 时,发送请求的过程被分为若干步骤,通常包括 Parse,Bind , Execute和Sync。本文主要关注 extened query

一、消息格式

  客户端和服务端所有通信都通过消息流进行。消息的第一个字节标识消息类型,随后四个字节标识消息内容的长度(该长度包括这四个字节本身),具体的消息内容由消息类型决定。

  openGauss 目前支持如下客户端消息类型:

c++
switch (qtype) { case 'a': /* Not reach : Trigger shipped to DN */ case 'A': /* AC WLM */ case 'b': /* Barrier */ case 'B': /* bind */ case 'c': /* copy done */ case 'C': /* close */ case 'D': /* describe */ case 'd': /* copy data */ case 'e': /* Thread ID */ case 'E': /* execute */ case 'f': /* copy fail */ case 'F': /* fastpath function call */ case 'g': /* GXID */ case 'G': /* PGXCBucketMap and PGXCNodeId */ case 'h': /* hybrid message query */ case 'H': /* flush */ case 'i': /* Instrumentation */ case 'I': /* Push, Pop schema name */ case 'j': /* Check gtm mode */ case 'J': /* Trace ID */ case 'k': /* Global session ID */ case 'K': /* client conn driver net_time */ case 'l': /* get and handle csn for csnminsync */ case 'L': /* Link gc_fdw */ case 'M': /* Command ID */ case 'n': /* Committing */ case 'N': /* Commit csn */ case 'o': /* role name */ case 'O': /* to reset openGauss thread in pooler stateless reuse mode */ case 'p': /* Process Pid */ case 'P': /* parse */ case 'q': /* Query ID */ case 'Q': /* simple query */ case 'r': /* Plan ID with sync */ case 'R': /* Reply collect info */ case 's': /* Snapshot */ case 'S': /* sync */ case 't': /* Timestamp */ case 'T': /* consistency point */ case 'u': /* AUTONOMOUS_TRANSACTION simple query */ case 'U': /* batch bind-execute */ case 'V': /* client conn driver support trace info*/ case 'W': /* WLM Control Group */ case 'w': /* dynamic WLM */ case 'x': /* imcs populate query */ case 'X': /* terminate */ case 'y': /* sequence from cn 2 dn */ case 'Y': /* plan with params */ case 'z': /* PBE for DDL */ case 'Z': /* simple plan */ default: break; }

  服务端收到如上消息的处理流程可以参考 PostgresMain。服务端发送给客户端的消息有如下类型(不完全):

java
switch (c) { case 'K': //receive dbTime case 'A': // Asynchronous Notify case '1': // Parse Complete (response to Parse) case 't': // ParameterDescription case '2': // Bind Complete (response to Bind) case '3': // Close Complete (response to Close) case 'n': // No Data (response to Describe) case 's': // Portal Suspended (end of Execute) case 'C': // Command Status (end of Execute) case 'D': // Data Transfer (ongoing Execute response) case 'E': // Error Response (response to pretty much everything; backend then skips until Sync) case 'I': // Empty Query (end of Execute) case 'N': // Notice Response case 'S': // Parameter Status case 'Z': // Ready For Query (eventual response to Sync) case 'G': // CopyInResponse case 'H': // CopyOutResponse case 'c': // CopyDone case 'd': // CopyData default: throw new IOException("Unexpected packet type: " + c); }

  客户端处理如上服务端消息的流程可以参考 PostgreSQL libqp 的实现 pqParseInput3

  openGauss 通信协议包括两个阶段: startup 阶段和常规 normal 阶段。 startup 阶段,客户端尝试创建连接并发送授权信息,如果一切正常,服务端会反馈状态信息,连接成功创建,随后进入 normal 阶段。 normal 阶段,客户端发送请求至服务端,服务端执行命令并将结果返回给客户端。客户端请求结束后,可以主动发送消息断开连接。

  normal 阶段,客户端可以通过两种 "子协议" 来发送请求,分别是 simple queryextened query。使用 simple query 时,客户端发送字符串文本请求,后端收到后立即处理并返回结果(gsql);使用 extened query 时,发送请求的过程被分为若干步骤,通常包括 Parse,Bind , Execute和Sync。

二、Extended Query 处理流程

  在 openGauss 中,Extended Query(扩展查询,JDBC默认协议) 是一种通过多个步骤拆分 SQL 执行过程的协议,主要用于参数化查询,能有效避免 SQL 注入、提升重复执行效率。Extended Query 不允许在一个请求中包含多条 SQL 命令,否则会报语法错误 Extended Query模式下JDBC会自动拆分一条语句的多个SQL) 。Extended Query 协议通常包括 5 个步骤,分别是 Parse,Bind,Describe(可选),Execute 和 Sync。以下分别介绍各个阶段的处理流程。

2.1. Parse(解析):生成语法树

  • 作用:客户端发送不带参数的 SQL 模板(如SELECT * FROM users WHERE id = $1),服务端收到该消息后,调用 exec_parse_message 函数进行处理,进行语法分析、语义分析和重写,同时会创建一个 Plan Cache 的结构,用于缓存后续的执行计划。
  • 关键操作
    • 检查 SQL 语法是否正确(如关键字拼写、语句结构)。
    • 验证表、列等对象是否存在(语义校验)。
    • 生成解析树,存储在服务器的内存中,并返回一个Parse Complete消息(包含解析树的标识,如PortalStatement ID)。
  • 示例:客户端发送Parse("SELECT * FROM users WHERE id = $1"),服务器返回解析成功的确认。

2.2. Bind(绑定):关联参数与执行计划

  • 作用:客户端发送参数值(如$1 = 123),服务器将参数与解析树绑定,生成执行计划(Execution Plan)。openGauss 收到该消息后,调用 exec_bind_message 函数进行处理。为之前保存的 Prepared Statement 创建执行计划并将其保存在 Plan Cache 中,创建一个 Portal 用于后续执行。

在 openGauss 内核中,Portal 是对查询执行状态的一种抽象,该结构贯穿执行器运行的始终。

  • 关键操作
    • 校验参数类型是否与表字段匹配(如id是整数,$1不能是字符串)。
    • 基于参数值和数据库统计信息(如索引分布),优化器生成最优执行计划(如选择索引扫描还是全表扫描)。
    • 生成一个Portal(可理解为 “执行上下文”),包含绑定后的执行计划,返回Bind Complete消息。
  • 示例:客户端发送Bind(参数: [123]),服务器将123$1绑定,生成针对id=123的执行计划。

2.3. Execute(执行):执行查询并返回结果

  • 作用:客户端触发执行已绑定的 Portal,服务器执行执行计划,返回结果。Execute 消息中可以指定返回的行数,若行数为 0,表示返回所有行。

  • 关键操作
    • 按照执行计划执行数据操作(如查询、插入、更新)。
    • 若为查询语句,将结果集分批返回给客户端(可通过Fetch控制返回行数)。
    • 若为写操作(如INSERT),执行事务日志记录和数据页更新。
  • 示例:客户端发送Execute(Portal ID),服务端收到消息后,执行 Bind 阶段创建的 Portal,执行结果通过 DataRow 消息返回给客户端,执行完成后发送 CommandComplete消息。msg示例:INSERT 0 1

2.4. Sync(同步):结束当前查询流程

  • 作用:客户端发送Sync消息,服务器清理本次查询的资源,结束当前会话的查询流程。
  • 关键操作
    • 释放 Portal 占用的内存资源(解析树、执行计划等)。
    • 向客户端返回ReadyForQuery消息,表明当前会话处于 “就绪状态”,可接收新的查询。
  • 示例:客户端发送Sync,服务器清理资源,等待下一次查询请求。

2.5. 批量管线与 Sync

  对于一次执行的多条SQL语句,JDBC会自动将语句拆分成多个单独的SQL语句,每条SQL语句都独立执行Parse/Bind/Describe/Execute流程

java
String sql = "INSERT INTO users (name, age) VALUES ('Alice', 30);select * from users where age=?;select * from users;select * from users;select * from users;select * from users;"; PreparedStatement pstmt = conn.prepareStatement(sql); pstmt.setInt(1, 30); pstmt.execute();

  • 为了减少往返延迟,JDBC 驱动通常将多条语句的 Parse/Bind/Describe/Execute 消息依次排队发送(pipelining),而不是一次一收一发,如果数据量小的话仅需一次RTT即可完成通信。
  • 当所有语句对应的 Execute 都发出后,客户端发送一个 Sync 消息,表示“这一批所有命令都发完了,请一次性返回结果,最后给我一个 ReadyForQuery”。
  • 服务器 → 客户端
    • 按照收到的命令顺序依次返回 ParseComplete、BindComplete、RowDescription(若有)、DataRow、CommandComplete……
    • 在最后,返回 ReadyForQuery,表示事务状态可查(空闲/活动/回滚),客户端可开始下一次交互。

  ‍

如果对你有用的话,可以打赏哦
打赏
ali pay
wechat pay

本文作者:司小远

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!