[Java,Sybase]jconn3.jar升级到jconn4.jar PreparedStatement Connection.prepareStatement(String)方法性能劣化

0x00 一句话

大量通过prepareStatement方法创建PreparedStatement实例的时候,如果预编译开关是开着的,那么每次调用都会产生和DBMS的通信,大量通信导致性能劣化。prepareStatement不是给大量调用而设计的,是为重复使用而设计的,尽量避免在预编译开关开着的时候大量调用prepareStatement。

0x01 前言

公司别组有项目是从Java1.6升级到Java1.8, 其中使用了Sybase的JDBC Driver(jconn3.jar)。这个jar包也需要从jconn3.jar升级到jconn4.jar。而升级之后出现了明显的性能下降。笔者参与了性能劣化原因的调查并查明了原因。

性能下降现象:
		jconn3.jar:  20s/万次插入。
		jconn4.jar:  70s/万次插入。

0x02 劣化代码定位

看到报告数字的时候笔者不敢相信升级会导致性能劣化这么严重,于是开始着手读代码。
大概定位到性能劣化的代码长相简化后如下。

    // Connection conn = ...;
    // List<String> sqlLists = ...;
    // sqlLists规模万以上。
	for (String sql : sqlLists)
	{
		PreparedStatement stmt = conn.prepareStatement(sql);//性能劣化行
		// Set parameters for stmt.
		// ...
		stmt.executeUpdate();
	}

对循环内部的几句代码做分析之后,分别测试了循环中几句关键代码的时间消耗之后,

PreparedStatement Connection.prepareStatement(String)
jconn3.jar: 0s/万次
jconn4.jar: 50s/万次

发现这个方法耗时严重,且刚好和劣化的时间能符合(50s/万次)。

0x03 PreparedStatement Connection.prepareStatement(String) 性能劣化原因分析

定位到了性能有问题的代码之后,开始着手分析为什么升级jconn会导致该方法性能劣化如此严重。
因为JDBC是一套标准接口,各个厂商会自己实现这套接口,jconn3.jar就是这套接口的一个实现(implement),而jconn4.jar是另一个实现。也就是说在实现这个接口方法的时候,Sybase提供的JDBC Driver内部实现变更了。
这就提供了两个调查方向给笔者。

  1. PreparedStatement Connection.prepareStatement(String)的JDBC标准定义[1]
  2. Sybase升级的时候对这个方法的实现做了什么改动。[2]

那么看一下prepareStatement的JDBC标准定义(文档)吧

prepareStatement JDBC doc
这里的Note里提到了,有的driver会使用预编译(precompilation),所以会在被调用的时候去把接收到的SQL送到DB端(DBMS)去做预编译,有的driver不会做这个事情。
这个描述非常符合笔者这次遇到的情况。可以几乎猜想到大量PrepareStatement被送到DBMS做预编译,大量通信所以导致性能劣化

0x04 Sybase 更新证明

上面笔者通过接口定义大致猜到了因为预编译的通信导致性能劣化,也就是说jconn3.jar的时候不会去做预编译,而jconn4.jar的时候会做预编译。报告的时候需要证据来证明笔者的结论,所以不得不去Sybase的官方文档翻…

Sybase PrepareStatement Doc
jConnect 6.05之后,该方法能够支持返回两种PreparedStatement,一种是预编译过,一种是未预编译过的。通过对Connection设置DYNAMIC_PREPARE属性指定。

  • DYNAMIC_PREPARE = True : 返回预编译的PreparedStatement。
  • DYNAMIC_PREPARE = False : 返回未预编译的PreparedStatement。

原来如此,笔者检查了一下别组的创建Connection的代码,发现并没有使用这个属性。
大概率是升级的时候这个属性默认值(Default Value)改变了。查了一下参考文献[3][4],果然…

jar jConnect DYNAMIC_PREPARE 默认值
jconn3.jar jConnect 6.05 False
jconn4.jar jConnect 7.0 False
jconn4.jar jConnect 7.07 True
jconn4.jar jConnect 7.07 SP100 True
jconn4.jar jConnect 7.07 SP110 True
jconn4.jar jConnect 16 True

另:如果想知道jconnX.jar内部的jConnect版本,执行这个jar即可。

	java -jar jconnX.jar

结果查明了,DYNAMIC_PREPARE 默认值变更导致了原代码性能下降。

0x05 感想

  1. PrepareStatement预编译可以在大量执行的时候提供非常好的性能优化。但是如果每次都去生成一个PrepareStatement并且该实例只用一次的话,不开预编译也许是更好的选择。
  2. 这个代码是前人写的,后被接手,更新性能劣化严重导致升级组的人被骂得很惨。结果是前人埋的巨坑,后面的人埋单。TMD, PrepareStatement不重复使用的小伙子你出来,看笔者不锤爆你的头…

最后希望大家都能提高自己的写码水平,避免不必要的时间损失。下手写码前多看官方文档和Best Practice,对于写出高质量高可读可维护的代码会有帮助。

参考文献:
[1] Connection, Oracle Corporation (2019/03/21) Retrieved https://docs.oracle.com/javase/jp/8/docs/api/java/sql/PreparedStatement.html
[2] Sybase documentation, SAP Corporation (2019/03/21) Retrieved http://infocenter.sybase.com/help/index.jsp?topic=/com.sybase.infocenter.help.ase.16.0/doc/html/title.html
[3]jConnect 6.0.5 - DYNAMIC_PREPARE connection property, SAP Corporation (2019/03/21) Retrieved http://infocenter.sybase.com/help/topic/com.sybase.infocenter.dc39001.0605/html/prjdbc/X30815.htm
[4]jConnect 16.0 - DYNAMIC_PREPARE connection property, SAP Corporation (2019/03/21) Retrieved http://infocenter.sybase.com/help/topic/com.sybase.infocenter.dc39001.1600/doc/html/san1353997987255.html

发布了24 篇原创文章 · 获赞 24 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/ToraNe/article/details/88703873