最近测试系统时遇到一个在并发压力下系统会生成重复“单号”的问题。
“单号”是一次业务操作时产生的序列号(重要,必须唯一),格式如:KI100126000001,KI为一类型号,100126为当天日期(年份取末2位),后面是流水帐号,不足6位补0,每日重新从1开始。系统是使用struts + spring +
hirbernate框架的,中间件是IBM
WebSphere 6.1(Win2003平台),数据库为Sybase 15(AIX平台)。
生成单号的方法是:
数据库中有一个SHIT_SERIALNO表,通过该表的序列号字段SERIAL_NO保存一个当前的流水帐号;
由系统使用存储过程proc_shitNo来生成工单号。存储过程每次select到SERIAL_NO值,再Update SERIAL_NO值为SERIAL_NO+1,返回SERIAL_NO+1的值作为工单号的后一部分。
系统生成工单号后,将工单号的记录插入到一个SHIT_WORKSHIT的表,WORKSHIT_NO为唯一索引。
主要的问题是:
在10并发压力下,系统似乎会不断地获取到相同的序列号,生成重复的工单号从而使得插入数据失败。(据开发人员介绍,系统代码里会尝试进行10次插入操作,10次都不成功则在WAS的日志输出“保存10次工单依然无法保存”,每一次都是重新执行存储过程,重新生成单号)。插入数据失败的情况:2000笔业务大约是丢失30~70个单号记录。TPS值约在20~30之间。
同一个生成工单的场景10并发压力时,对WAS服务器的CPU压力较大,CPU平均使用约60~70%(这是优化了一段时间后的结果),数据库的压力较小。其它磁盘网络等未见有瓶颈或异常。
在保存单号失败时,WAS服务器记录的LOG如下:
26.1.2010 12:50:42
com.csair.callcenter.business.impl.WorksheetNoPrcoManagerImpl
executeSPOfJndi
ERROR: ConnectionCallback; uncategorized SQLException for SQL
[]; SQL state [null]; error code [0]; DSRA9110E: 关闭 Connection。; nested
exception is com.ibm.websphere.ce.cm.ObjectClosedException: DSRA9110E: 关闭
Connection。
org.springframework.jdbc.UncategorizedSQLException:
ConnectionCallback; uncategorized SQLException for SQL []; SQL state [null];
error code [0]; DSRA9110E: 关闭 Connection。; nested exception is
com.ibm.websphere.ce.cm.ObjectClosedException: DSRA9110E: 关闭
Connection。
at
org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate
......
at
com.csair.callcenter.business.impl.WorksheetNoPrcoManagerImpl$1.doInConnection(WorksheetNoPrcoManagerImpl.java:55)
at
org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:342)
...
59 more
[10-1-26 12:50:42:660 CST] 0000005f
Batch E
org.directwebremoting.util.CommonsLoggingOutput error A request has been denied
as a potential CSRF attack.
[10-1-26 12:50:42:660 CST] 00000069
SystemOut O 26.1.2010 12:50:42
com.csair.callcenter.business.impl.WorksheetManagerImpl
createWorksheet
ERROR:
保存单号失败
com.csair.callcenter.business.exception.BusinessException: 创建单号失败
......
[10-1-26 12:50:42:723 CST] 00000036 SystemOut O
26.1.2010 12:50:42 com.csair.callcenter.business.impl.CallinManagerImpl
startUpCallin
ERROR:
创建单号失败
com.csair.callcenter.business.exception.BusinessException:
保存10次单号依然无法保存
......
网上和同事分析:造成DSRA9110E的原因一般是由于应用在执行数据库操作时jdbc数据库连接已经处于关闭状态。
但为什么系统正在运行时WebSphere的数据库连接会处于关闭状态呢?大家都不得其解!我一直以来很少做开发,压力测试时也不接触代码。而且一段时间下来,心烦气燥的,且在领导一日一报告的压力下,冷静分析理智思考的能力早失,嘿,不过似乎也一直未拥有过。
最后开发人员发现,系统代码中调用存储过程的方法前缺少了synchronized关键字来保证代码的线程安全,只需要在方法前添加一synchronized关键字即可。
这一问题算是解决了,但为何线程不安全会使应用在执行数据库操作时jdbc数据库连接已经处于关闭状态???
发疯乱猜一下:10并发下,多次获取到重复单号并插入数据库失败,因为单号是唯一索引。每一个单号插入失败即会for循环生成10次并尝试插入。确定的30次丢失单号“保存10次单号依然无法保存”进行的保存操作是300次,另外的1970笔单号没有丢失,但无法确定它们是第一次生成就插入成功的,还是第9次尝试才生成单号并插入成功。如果计算取尝试平均值5,那就是9850次失败。哈,那插入操作进行了10150次才完成。呵呵,这样看来,部分连接处于关闭状态应该相应会较正常。
使用synchronized可以保证数据一致性问题,因为同一时间只有一个线程在执行,理论上会对性能有一定影响。但实际测试中,只生成单号场景10并发压力,使用synchronized关键字后,平均响应时间0.021秒,TPS平均值384.6;不使用synchronized关键字,平均响应时间0.172秒,TPS平均值43.8。原因分析为:不使用synchronized时,多次出现“保存10工单依然保存失败”的情况,可能大大增加了响应时间。
上面的修改过程中,使用过sybase官方的jdbc3驱动和第三方的jtds进行测试。在解决丢单问题后,面对更大压力(30并发)时,使用jtds驱动会偶尔出现如下错误:
[10-1-27 12:18:40:129 CST] 0000023a ConnectionEve A J2CA0056I: 连接管理器接收到来自资源
jdbc/ccs_jtds 的资源适配器的致命连接错误。接收到的异常为
com.ibm.websphere.ce.cm.StaleConnectionException: There is not enough procedure cache to run
this procedure, trigger, or SQL batch. Retry later, or ask your SA to
reconfigure ASE with more procedure
cache.
:java.sql.SQLException: There is not enough procedure
cache to run this procedure, trigger, or SQL batch. Retry later, or ask your SA
to reconfigure ASE with more procedure cache.
而同等压力下使用jdbc3则无此现象,系统运行正常不会出现procedure cache不足的情况。是否可从某一侧面上表明使用sybase官方的驱动程序jdbc3比jtds在数据库内存消耗这一方面上更优?!哎,这种的证明真是麻烦。
原文地址:http://blog.sina.com.cn/s/blog_477759e70100gk6c.html
添加新评论0 条评论