" />
文章

pgjdbc与PostgreSQL数据库的通信机制

pgjdbc与PostgreSQL数据库的通信机制

PostgreSQL JDBC驱动(pgjdbc)与PostgreSQL数据库服务器之间的通信采用基于TCP/IP的socket连接,使用的是PostgreSQL专有的前端/后端协议。从提供的代码上下文看,pgjdbc实现的是该协议的v3版本(QueryExecutorImpl.java在v3包下)。

通信方式与协议概述

通信方式

  • 基于TCP/IP的socket通信
  • 全双工通信:客户端和服务器可以同时发送和接收消息
  • 消息驱动:通过交换格式化的二进制消息进行通信

协议版本

// org.postgresql.core.v3.QueryExecutorImpl.java
@Override
public int getProtocolVersion() {
  return 3;  // 协议版本3
}

PostgreSQL前端/后端协议v3是从PostgreSQL 7.4版本引入的,现在的PostgreSQL服务器普遍支持此版本,也是pgjdbc默认使用的协议版本。

协议特点

1. 消息结构

每个消息都有特定的格式,通常包括:
- 单字节消息类型标识符
- 4字节长度字段(包括长度本身)
- 消息内容

// 发送简单查询消息示例
pgStream.sendChar('Q');  // 消息类型'Q'表示简单查询
pgStream.sendInteger4(length);  // 消息长度
pgStream.sendString(sql);  // SQL文本

2. 两种查询模式

协议支持两种主要的查询执行模式:

简单查询模式

// 在QueryExecutorImpl.java中
if ((flags & QueryExecutor.QUERY_EXECUTE_AS_SIMPLE) != 0) {
  // 简单查询模式
  pgStream.sendChar('Q');  // 简单查询消息类型
  pgStream.sendString(query.toString(parameters));  // 完整SQL语句
}

这种模式简单直接,一条消息包含整个SQL语句。

扩展查询模式

// 在QueryExecutorImpl.java中
else {
  // 扩展查询模式,分多步执行
  sendParse(query, parameters);  // 解析阶段
  sendBind(query, parameters);   // 绑定阶段
  sendExecute();                 // 执行阶段
  sendSync();                    // 同步
}

扩展查询模式将SQL执行拆分为多个步骤,支持预编译和参数绑定。

3. 二进制数据支持

协议支持文本和二进制两种数据格式,可以大幅提高性能:

// 在PgConnection.java中
public boolean binaryTransferSend(int oid) {
  return queryExecutor.useBinarySendForOid(oid);
}

4. 异步操作支持

协议支持非阻塞操作,允许取消正在执行的查询:

// 在QueryExecutorImpl.java中
@Override
public void sendQueryCancel() throws SQLException {
  PGStream cancelStream = null;
  try {
    // 创建取消请求
    cancelStream = cancelQuery();
  } finally {
    if (cancelStream != null) {
      try {
        cancelStream.close();
      } catch (IOException e) {
        /* ignore */
      }
    }
  }
}

5. 服务器端预编译

支持将SQL语句在服务器端预编译,提高重复执行的性能:

// 在QueryExecutorImpl.java中
private void sendParse(Query query, V3ParameterList parameters) throws IOException {
  // 给语句一个名称,以便重用
  String statementName = query.getStatementName();

  // 发送Parse消息
  pgStream.sendChar('P');
  pgStream.sendString(statementName);
  pgStream.sendString(query.getNativeSql());

  // 参数类型信息
  pgStream.sendInteger2(parameters.getTypeOIDs().length);
  for (int i = 0; i < parameters.getTypeOIDs().length; i++) {
    pgStream.sendInteger4(parameters.getTypeOIDs()[i]);
  }
}

主要消息类型

客户端到服务器的消息

  • Q: 简单查询
  • P: 解析(Parse)
  • B: 绑定(Bind)
  • E: 执行(Execute)
  • D: 描述(Describe)
  • S: 同步(Sync)
  • X: 终止(Terminate)

服务器到客户端的消息

  • R: 认证请求
  • T: 行描述(Row Description)
  • D: 数据行(Data Row)
  • C: 命令完成(Command Complete)
  • E: 错误(Error)
  • N: 通知(Notice)
  • Z: 准备接收查询(Ready For Query)

错误和异常处理

协议提供了详细的错误报告机制:

// 在QueryExecutorImpl.java中
private SQLException receiveErrorResponse() throws IOException {
  // 读取错误字段
  ServerErrorMessage serverError = new ServerErrorMessage(pgStream);

  // 转换为SQLException
  return new PSQLException(serverError);
}

总结

PostgreSQL的前端/后端通信协议具有以下优势:

  1. 高效:支持二进制数据传输和服务器端预编译
  2. 灵活:提供简单和扩展两种查询模式
  3. 强大:支持异步操作、批处理和事务管理
  4. 类型安全:提供详细的类型系统和类型转换
  5. 详细的错误报告:提供结构化的错误信息

pgjdbc驱动的核心任务就是封装这一复杂协议,为Java应用程序提供符合JDBC规范的简洁接口,同时充分利用PostgreSQL特有的功能和性能优化。

License:  CC BY 4.0