pgjdbc源码分析

今天我们讲一讲pgjdbc。pgjdbc是postgresql的JDBC接口。其网址是https://jdbc.postgresql.org/.是开源软件,我们可以轻松的查看其代码,理解jdbc的工作原理。


一. 源代码目录结构

pgjdbc的源码结构如下图:

那么我们来一一看看各个模块都是做什么的吧。

1 core

该目录是程序的核心模块目录。

这里实现了大部分pgjdbc的基类和接口,例如statement query log type command connection类等等;

并且在此基础上的Connection的V3协议(V2版本的协议在新版本里已删除);

2 data source

该模块实现的是jdbc的数据源模块。其中:

ds目录下提供基础的数据源支持;

xa目录下提供支持分布式事务的数据源;

3 common api

jdbc
jdbc2
jdbc3
Driver.java
这些目录下提供了共通的jdbc驱动接口。

4 extend api

这部分是postgresql独有的特殊命令和类型的支持。其中:

copy目录下的代码支持postgresql的COPY命令;

geometric目录下的代码 支持postgresql的集合类型;

fastpath目录下的代码是PostgreSQL提供一种快速路径接口来向服务器发送简单的函数调用。这个接口在某种程度上已被废弃,因为我们可以通过创建一个定义该函数调用的预备语句来达到类似或者更强大的功能;

largeobject目录下的代码提供对Postgresql的大对象数据类型的支持;

5 security api

ssl sspi gss这些很显然,这是对安全相关的认证的支持。

6 until

该目录下的代码提供一些工具库的实现。例如message类、 exception类

7 hostchooser

该目录下的代码提供了对连接字符串中targetServerType参数的支持。支持特定类型的host的选择(目前指定该参数为preferSlave有点bug)。

8 osgi

目录下的代码实现了OSGi(Open Service Gateway Initiative)技术是Java动态化模块化系统的一系列规范(老实说并不是太懂)。


二. 调用关系

调用关系的话,我们大家应该还是蛮熟悉的。不多说,举例子吧。

假设我们执行这样一个查询:

select * from hr.employees;

pgjdbc的工作时序大概下面这个样子(在网上找的图,侵删):

这里面唯一神秘的就是executeQuery()执行后,客服端和database的交互细节。下面我们简单的分析下这一段的协议流吧。


三. 协议流

PostgreSQL的客户端与服务端通过协议消息通信,下面以pgjdbc执行一个简单的SELECT为例说明

已定义以下的表。

create table ecpg_test(tint int, name text);
insert into ecpg_test values (12,'qwe');

执行的SELECT语句

select * from ecpg_test where tint =12

SELECT执行的流程
我们用一个java程序去连接postgresql。为了追踪log,我们如下设置:

1.客户端:连接字符串里面加上参数loggerLevel=TRACE来追踪这次连接以及查询
2.服务端:设置参数client_min_messages和log_min_messages为debug5(这里我为了log完整起见,其实没必要设置这么高)

运行完程序,log如下(为方便观察,我就摘抄了一部分,其它的我省略了):

.
.
.
11 14, 2017 3:26:27 午後 org.postgresql.core.v3.QueryExecutorImpl execute
FINEST:   simple execute, handler=org.postgresql.jdbc.PgStatement$StatementResultHandler@19d449fc, maxRows=0, fetchSize=0, flags=17
11 14, 2017 3:26:27 午後 org.postgresql.core.v3.QueryExecutorImpl sendParse
FINEST:  FE=> Parse(stmt=null,query="select * from ecpg_test where tint =12",oids={})
11 14, 2017 3:26:27 午後 org.postgresql.core.v3.QueryExecutorImpl sendBind
FINEST:  FE=> Bind(stmt=null,portal=null)
11 14, 2017 3:26:27 午後 org.postgresql.core.v3.QueryExecutorImpl sendDescribePortal
FINEST:  FE=> Describe(portal=null)
11 14, 2017 3:26:27 午後 org.postgresql.core.v3.QueryExecutorImpl sendExecute
FINEST:  FE=> Execute(portal=null,limit=0)
11 14, 2017 3:26:27 午後 org.postgresql.core.v3.QueryExecutorImpl sendSync
FINEST:  FE=> Sync
.
.
.
11 14, 2017 3:26:27 午後 org.postgresql.core.v3.QueryExecutorImpl processResults
FINEST:  <=BE ParseComplete [null]
11 14, 2017 3:26:27 午後 org.postgresql.core.v3.QueryExecutorImpl processResults
FINEST:  <=BE BindComplete [unnamed]
11 14, 2017 3:26:27 午後 org.postgresql.core.v3.QueryExecutorImpl receiveFields
FINEST:  <=BE RowDescription(2)
11 14, 2017 3:26:27 午後 org.postgresql.core.v3.QueryExecutorImpl receiveFields
FINEST:         Field(tint,INT4,4,T)
11 14, 2017 3:26:27 午後 org.postgresql.core.v3.QueryExecutorImpl receiveFields
FINEST:         Field(name,TEXT,65535,T)
11 14, 2017 3:26:27 午後 org.postgresql.core.v3.QueryExecutorImpl processResults
FINEST:  <=BE DataRow(len=5)
11 14, 2017 3:26:27 午後 org.postgresql.core.v3.QueryExecutorImpl receiveCommandStatus
FINEST:  <=BE CommandStatus(SELECT 1)

11 14, 2017 3:26:27 午後 org.postgresql.core.v3.QueryExecutorImpl receiveRFQ
FINEST:  <=BE ReadyForQuery(I)
.
.
.
11 14, 2017 3:02:20 午後 org.postgresql.core.QueryExecutorBase close
FINEST:  FE=> Terminate

Process finished with exit code 0

可以看到,客户端在执行execute时,依次进行了sendParse、sendBind、sendDescribePortal、sendExecute、sendSync这些操作。

然后服务端对应这些操作分别对应给了处理和相应:ParseComplete、BindComplete、RowDescription、DataRow等等。

这些操作写在org.postgresql.core.v3包下的QueryExecutorImpl.java中。更详细一点的说,是在函数中:

private void sendOneQuery(SimpleQuery query, SimpleParameterList params, int maxRows,
      int fetchSize, int flags) 

其实这里有一个疑问,在sendOneQuery()函数里,上面所说的客户端和服务端操作是按照次序一一对应的执行的。手画一个草图比如:

  client  ------------------   ---  server
   |sendParse()                        |
   |   -------------------------->     |
   |   <-------------------------      |
   |                     ParseComplete |
   |sendBind()                         |
   |   ------------------------->      |
   |   <-------------------------      |
   |                     BindComplete  |
.
.
.

也就是说,是有严格的次序关系的。可是我们看log可以发现,客户端的这些操作是一次性发到服务端,服务端也是处理完一次性发回的。那么为什么会不一致呢?

请教了朋友和同事,原来是:

为了优化网络传输。将多次发送的数据集中到一次发送,提高效率。

好的,今天就到这里。

引用:
http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=20726500&id=4150218

posted @ 2017-11-14 22:40 非我在 阅读(...) 评论(...) 编辑 收藏