这几天给项目做性能压力测试,发现一个方法压力200之后就会把整个系统弄停掉。仔细检查发现是开发人员调用数据库的写法有问题。用的是spring的jdbcTemplate,在使用回调的时候,在回调里又做了数据库的查询。只要把这个查询写在回调外执行就没有性能问题,写在里面压力大的时候马上出问题。

  查看spring的源代码发现,这两种写法唯一的区别就是,写在回调里面的时候,数据库连接未关闭就开启一个新的连接进行操作,写在外面则是先关闭了的。

  造成数据库端报错ORA-12519,数据库端给不出可用的连接来。分析这种写法造成数据库连接资源浪费有两个方面,第一,一个线程打开了两个连接,第一个连接必须等第二个连接完成了才能释放,也就是直观上连接资源占用加倍了。第二在时间上,第一个连接不能立即释放,造成其他线程获取可用连接的机会更少了。

  为了保证不出这样的性能问题,(不能确保每个开发程序员都不这样写,防止万一,因为这样写造成的性能瓶颈太明显了)决定将springJdbc封装一层,因为ResultSet必须依赖与链接,如果链接先关闭,就取不到数据了,所以在使用回调的时候,先将结果集保存在内存中,然后马上关闭链接,再用保存在内存中的结果集来进行数据的提取。

  这个想法是有了,实现前提是要了解ResultSet的数据结构,才好将数据转移到保存在内存中这个结果集中,其次是要将这个内存中的结果集也是java.sql.ResultSet的实现,这样对开发人员来说才是透明的。想着工作量是挺大的,还好有现成的接口了javax.sql.rowset.CacheRowSet,还有现成实现--->com.sun.rowset.CacheRowSetImpl,直接使用就OK了。

  以为是ok了,发现用orcale的时候会报一个数字类型精度的错误,网上有很多种解决办法,有在数据库驱动层改的,有在Spring改的,有在连接池包里改delegate的,但我不想改第三方jar包,这样部署的时候就必须用我们修改过的了,我就在封装jdbcTemplate那一层用DataSouce做判断,目前spring用的数据库连接池主要有三种,DBCP、c3p0、proxool。很遗憾DataSouce接口没提供访问,所以我不得不用反射的方式获取驱动类名。DBCP的方法是getDriverClassName;c3p0的方法是getDriverClass;proxool的方法是getDriver,这样就可以获得数据库驱动的类型了,再判断是不是orcale的驱动,如果是就使用orcale自己提供的CacheRowSet实现-->oracle.jdbc.rowset.OracleCachedRowset,如果不是还是用sun提供的实现。

  完成。

*ps,当时在想的时候还有一个思路就是线程与链接进行绑定,一个线程只允许开一个链接,在获取链接的时候先看线程局部对象里绑定的链接是否关闭,如果没关闭就用这个链接,不再新开一个链接,这个思路比较大胆,对项目里数据库调用的具体情况还不清楚,所以只是假设,但这个的实现都必须是在数据库连接池管理上去实现,所以必须是数据库连接池支持才可以,就看了spring用的三种连接池,都没有这样的配置,说明这种想法业界没有用过,既然如此就打消这种想法了。

------------------------------------------------分割线-----------------------------------------------------

用了几天又发现问题了,就是Oracle的rowSet实现类OracleCachedRowset不能自动类型转换,原来我们用的DBCP的DelegatingResultSet可以转字符串"0"直接转化成boolean型的false,而OracleCachedRowset就不行会报错,但我们项目里已经大量用了这种自动类型转换的方式了,没有办法只能放弃使用OracleCachedRowset,还是只能想办法规避oracle的精度返回-127的问题。

网上说的办法是用javaassist修改一下OracleResultsetMetaData的getScale方法。是Oracle9的驱动。我用反编译器打开看,有些不一样,我们用的10g的驱动,代码里做了一些判断,如果精度是-127并且oracle.jdbc.J2EE13Compliant参数是ture就返回0,说明Oracle意识到了这个bug,现在我要做的就是要将oracle.jdbc.J2EE13Compliant这个参数写到OracleDriver里,我一路查源代码,oracle驱动包是公布了修改这个参数的接口的,dbcp也是有这个接口的,但spring没有,spring为了通用性放弃了特殊需求,所以如果想在spring配置里加入这个参数,那就得修改spring的源码,这种方式是我最不愿意的。所以采用另一种简单的方法,oracle驱动也做得很好,会判断初始化OracleDriver传入的参数里有没有这个参数,如果没有会去系统参数里面去取。这样就很简单了,只要在我之前判断是不是oracle数据库那里加上一句代码System.setProperty("oracle.jdbc.J2EE13Compliant","true")就可以了。希望不要再出问题了。目前只是把结果集保持在内存中了,还有调用数据库存储过程的形势没解决呢。

------------------------------------------------分割线-----------------------------------------------------

问题又出了,报Invalid precision Value .Cannot be less than zero的错误,查看oracle.jdbc.driver.OracleResultSetMetaData的getPrecision方法,发现会去找describeType属性,如果是112或者113就返回-1,现在我如果是blob或者clob的字段就会返回-1,RowSetMetaDataImpl不接受负数的值作为precision,就报错了。describeType怎么来的呢,是由oralce.jdbc.driver.T4CTToac里的 oacdty字段来的,这个是Oacle Data Type的意思,查看下面的表

Oracle 10.2 Data Types

Oracle 10.2 data types (oacdty) for use when you're (bravely) exploring an Oracle trace:

1     VARCHAR2 or NVARCHAR2 
2     NUMBER 
8     LONG 
9     NCHAR VARYING, VARCHAR 
12   DATE 
23   RAW 
24   LONG RAW 
25   LONG UB2 
26   LONG SB4 
58   ANYDATA 
69   ROWID 
96   CHAR or NCHAR 
100 BINARY FLOAT 
101 BINARY DOUBLE 
102 REF CURSOR 
104 UROWID 
105 MLSLABEL 
106 MLSLABEL 
111 XMLTYPE (TABLE or REF) 
112 CLOB or NCLOB 
113 BLOB 
114 BFILE 
121 TYPE (USER-DEFINED) 
122 TYPE (TABLE OF RECORD) 
123 TYPE (VARRAY) 
178 TIME 
179 TIME WITH TIME ZONE 
180 TIMESTAMP 
181 TIMESTAMP WITH TIME ZONE 
182 INTERVAL YEAR TO MONTH 
183 INTERVAL DAY TO SECOND 
208 UROWID 
231 TIMESTAMP WITH LOCAL TIME ZONE

所以我的CLOB、BLOB 字段precision会返回-1了,这个写死的了,没有办法在哪里改配置了。用OacleCachedRowSet是可以的,但基于之前的原因但又不能用。查英文资料说

DataDirect JDBC Driver 可以用,就找了一个来,加入相应的jar包,然后class.forName("com.ddtek.jdbc.oracle.OracleDriver");url="jdbc:datadirect:oracle://IP:1521;DataBaseName=orcl"。一切OK结果却报:this driver is locked for use with embedded applications。谷歌了下说需要license。我只是用jar放入我的项目中,根本不想安装datadirect的客户端,更懒得去搞认证,干脆继续用反编译找到com.ddtek.jdbc.base.BaseConnection的open方法,就是在这里给属性lockedEmbedding赋值了,只要把这个属性弄成false就可以通过了。这里调用了一系列方法赋值,我没继续看下去了,就准备在这个做一个切点,直接给false值。简单的学习了下JAVAssist的使用,来改这个字节码文件,得到我想要的false值。

public static void main(String[] args) throws Exception {
ClassPool pool = ClassPool.getDefault();
try {
pool.insertClassPath("D:\\base.jar"); //将jar包放在D盘下的
CtClass cc = pool.get("com.ddtek.jdbc.base.BaseConnection");
CtMethod m = cc.getDeclaredMethod("open");
m.instrument(new ExprEditor(){
        @Override
        public void edit(MethodCall m)throws CannotCompileException {
          if(m.getClassName().equals("com.ddtek.jdbc.base.BaseLicenseUtility")&&m.getMethodName().equals("isLocked")){
            m.replace("$_=false;");
          };
        }
});
cc.writeFile("D:\\test");
} catch (Exception e) {
e.printStackTrace();
} }

然后把这个生成的class文件替换jar包里原来那个class文件就可以了。只是很粗略的学习了下JAVAssist,还有很多很强大的功能,也肯定有比我这种写法更好的,效果都差不多,只是可读性更好,运行成功,原来破解软件也没想象中那么高端嘛。

数据库连接不关闭造成的问题以及RowSet的使用的更多相关文章

  1. 记一次排查mysql数据库连接未关闭问题的过程

    在一些项目中由于一些特殊原因仍然保留着显示的获取数据库连接(Connection).提交事务.回滚事务.关闭连接等操作:其中关闭连接是比较容易疏忽又比较难在前期发现的问题. 我是如何排查连接未关闭的问 ...

  2. 数据库连接未关闭,conn与rs未关闭

    场景: 2000多人使用系统,早上打卡签到,时间点比较集中. 程序:会创建connction连接.但是未关闭,导致tomcat挂了.导致连接池已满 解决:conn.close,rs.close.记住一 ...

  3. 6. Connection has already been closed 数据库连接被关闭

    生产上Tomcat出现 Connection has already been closed.问题,但是在uat测试是好的! 遇见两次: 1.某个程序dao中执行逻辑异常复杂,有时候需要执行一分多钟, ...

  4. JDBC建立/关闭数据库连接

    JDBC(Java Data Base Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口 ...

  5. Java学习-006-三种数据库连接 MySQL、Oracle、sqlserver

    此文主要讲述在初学 Java 时,常用的三种数据库 MySQL.Oracle.sqlserver 连接的源代码整理.希望能对初学 Java 编程的亲们有所帮助.若有不足之处,敬请大神指正,不胜感激!源 ...

  6. DbUtil数据库连接

    DbUtil数据库连接 package com.zjx.util; import java.sql.Connection; import java.sql.DriverManager; public ...

  7. 关于打开现有项目时数据库连接配置遇到的问题 连接字符串中的数据源值指定未安装的SQL Server的实例。要解决此问题,可选择安装匹配的SQL Server实例或修改连接字符串中的数据源值

    最近在看红皮书<ASP.NET MVC 5 高级编程>时,为了更好理解,边看书,边打开源代码查看,在VS(Visual Studio 2015)中将源代码打开,发现数据库连接是关闭的,本想 ...

  8. JBDC—③数据库连接池的介绍、使用和配置

    首先要知道数据库连接(Connection对象)的创建和关闭是非常浪费系统资源的,如果是使用常规的数据库连接方式来操作数据库,当用户变多时,每次访问数据库都要创建大量的Connnection对象,使用 ...

  9. 原生Orcale数据库连接

    package tj.test.demo; import java.sql.Connection;import java.sql.DriverManager;import java.sql.Prepa ...

随机推荐

  1. python之初识网络

    一. C/S架构:客户端(client)/服务端(server)架构 B/S架构:浏览器(browser) / 服务端(server)架构 软件cs架构: 浏览器,qq,微信等等 硬件cs架构:打印机 ...

  2. python之正则表达式及RE模块

    正则表达式(匹配字符串)web界面正则匹配工具:http://tool.chinaz.com/regex/ 元字符 1 . 匹配除换行符之外的任意字符 2 \w 匹配数字字母下划线 3 \d 匹配数字 ...

  3. python之成员(面向对象)

    1. 成员 在类中你能写的所有内容都是类的成员 class Person: def __init__(self, name, gender): self.name = name # 成员 self.g ...

  4. vue-cli脚手架之build文件夹上半部

    好,接下来一起分析分析配置文件^o^. build.js作用:命令npm run build的入口配置文件,主要用于生产环境. build.js中具体含义标注(vue-cli脚手架官方文件解释,大家可 ...

  5. Docker第二章:docker基础1--镜像,容器&仓库

    镜像介绍及操作:http://www.haveneed.cn/article-detials/115 容器介绍及操作:http://www.haveneed.cn/article-detials/11 ...

  6. GIS性能策略

    当一个地理平台上线运行,我们经常会遇到这些问题:1.系统刚上线时速度较快,一段时间后访问较慢?2.在地理平台目前的配置下,发布多少个服务才合理?一个服务配置多少个实例数才合适?这些问题,都涉及整个地理 ...

  7. Linux常用系统命令

    致歉:各位看到此博客的朋友们 因为命令的数量挺多的很多命令也都很简单  我就总结了一下具体的命令和这个命令是做什么的,主要的使用方法是链接到http://man.linuxde.net/的网站的,请各 ...

  8. Expo大作战(三十五)--expo sdk api之Location!

    简要:本系列文章讲会对expo进行全面的介绍,本人从2017年6月份接触expo以来,对expo的研究断断续续,一路走来将近10个月,废话不多说,接下来你看到内容,讲全部来与官网 我猜去全部机翻+个人 ...

  9. JavaScript大杂烩6 - 理解JavaScript中的this

    在JavaScript开发中,this是很常用的一个关键字,但同时也是一个很容易引入bug的一个关键字,在这里我们就专门总结一下页面中可能出现的this关键字(包括几种在其他页面文件中出现的this) ...

  10. 【HANA系列】SAP HANA XS使用服务器JavaScript Libraries详解

    公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[HANA系列]SAP HANA XS使用服务器 ...