在 WebSphere JDBC Adapter 中如何避免死锁和锁超时

在 WebSphere JDBC Adapter 中如何避免死锁和锁超时


转自 http://www.ibm.com/developerworks/cn/websphere/library/techarticles/1005_yuanjs_jdbcadapter_lock/

引言

WebSphere JDBC Adapter 是一个提供 J2EE 应用和数据库供应商的 EIS 解决方案之间的连接的资源适配器,是构建在 WebSphere Process Server 之上的 WebSphere Adapter。不用应用之间的数据交换发生在数据库层次,Adapter 使用 SQL 语句或存储过程以业务对象 (Business Object, BO) 的形式来传递数据,实现数据库与其他应用系统的集成。

WebSphere JDBC Adaper 可以集成任意构建在使用 JDBC 驱动(JDBC2.0 以上版本)的数据库上的企业应用,基于 J2EE JCA 技术实现了 JDBC API,提供了 Inbound 和 Outbound 两种操作来连接数据库。其中,在 Outbound 操作下,BO 从应用系统传递到数据库中,根据 BO 中指定的动作(Create, Update, Delete, Retrieve, , RetrieveAll, Execute, Exists),BO 作为一个请求发送到 Adapter 中,Adapter 将 BO 传递到具体数据库应用的表中。如果有需要,还可以进一步传递到其他构建在这个数据库之上的应用系统中,并进行相应处理。在 outbound 操作中,将会涉及到数据库的事务操作处理,继而会产生数据库的死锁和锁超时问题。接下来,看看死锁和锁超时分别在怎样的场景下会产生。





并发调用 WebSphere JDBC Adapter 的场景导致死锁现象

当 WebSphere JDBC Adapter 在 outbound 组件被并发调用的情况下,很可能产生死锁。在图 1 中,我们可以看到 outbound 组件的 create,delete 操作被并发的调用很多次。更为普遍的是,当多次发起用户请求的时候,会导致多线程场景。例如,outbound 组件在一个工作流中被使用,用户的多请求导致多个工作流并发的运行起来,也导致 outbound 组件也处在一个并发调用的情况下。


图 1. BPEL 中的并发情况
图 1:BPEL 中的并发情况


当 outbound 组件在并发调用比较高的情况下,会抛出下面死锁的异常信息:


清单 1. 死锁异常信息




[12/30/09 13:23:22:078 CST] 00000055 FFDC
Z com.ibm.ws.sca.internal.j2c.J2CMethodBindingImpl
com.ibm.ws.sca.internal.j2c.J2CMethodBindingImpl#0x04 Exception:
  javax.resource.ResourceException: javax.resource.ResourceException:
   DB2 SQL Error: SQLCODE=-911, SQLSTATE=40001,
   SQLERRMC=2, DRIVER=3.50.152, error code: -911
at com.ibm.j2ca.jdbc.commands.JDBCDeleteCommand.execute(JDBCDeleteCommand.java:379)
at com.ibm.j2ca.extension.commandpattern.CommandForCursor.execute
(CommandForCursor.java:68)
at com.ibm.j2ca.extension.commandpattern.Interpreter.
     executeWithChildren(Interpreter.java:96)
at com.ibm.j2ca.extension.commandpattern.Interpreter.execute(Interpreter.java:77)
at com.ibm.j2ca.jdbc.JDBCInteraction.executeInternal(JDBCInteraction.java:358)
at com.ibm.j2ca.jdbc.JDBCInteraction.execute(JDBCInteraction.java:139)
at com.ibm.ws.sca.internal.j2c.J2CMethodBindingImpl.invoke(
J2CMethodBindingImpl.java:242)









在特定场景下产生锁超时

在图 2 的场景之下,WebSphere JDBC Adapter 产生的两个 outbound 组件加入到两个不同的 BPEL 组件中。在这两个 BPEL 组件中,“BPELGlobalTransaction”组件使用了全局事务,另外一个“BPELLocalTransaction”组件使用了本地事务。在 WebSphere JDBC Adapter 产生的这两个 outbound 组件中,使用全局事务的“globaltransaction”outbound 组件完成删除主键为“1”的记录的行为,另外一个使用本地事务的“localtransaction”outbound 组件则要完成创建一个主键为“1”的记录的行为。在这个场景中,使用了 DB2 数据库,其数据库参数死锁检查时间和锁超时检查时间分别是“10”秒和“120”秒:

Interval for checking deadlock (ms) (DLCHKTIME) = 10000

Lock timeout (sec) (LOCKTIMEOUT) = 120


图 2.在事务中运行的 WebSphere JDBC Adapter 的 outbound 组件
图 2 在事务中运行的 WebSphere JDBC Adapter 的 outbound 组件


“BPELGlobalTransaction”组件的内部业务逻辑比较复杂,在实际运行中要 5 分钟左右时间才能够完成。但是“BPELLocalTransaction”组件却比较简单,只需要几秒的时间就可以结束。首先启动“BPELGlobalTransaction”组件的运行,然后再启动“BPELLocalTransaction”组件的运行,大概 2 分多钟之后,WebSphere Process Server 报告如下锁超时的异常:


清单 1. 锁超时异常信息



[12/30/09 11:38:29:062 CST] 00000072 FFDC  Z
com.ibm.ws.sca.internal.j2c.J2CMethodBindingImpl
com.ibm.ws.sca.internal.j2c.J2CMethodBindingImpl#0x04 Exception:
javax.resource.ResourceException:
DB2 SQL Error: SQLCODE=-911, SQLSTATE=40001, SQLERRMC=68, DRIVER=3.50.152,
error code: -911
at com.ibm.j2ca.jdbc.JDBCDBOperationHandler.executePreparedCUDStatement
(JDBCDBOperationHandler.java:216)
at com.ibm.j2ca.jdbc.JDBCDBOperationHandler.executeSQL(
JDBCDBOperationHandler.java:646)
at com.ibm.j2ca.jdbc.commands.JDBCCreateCommand.execute(
JDBCCreateCommand.java:309)
at com.ibm.j2ca.extension.commandpattern.CommandForCursor.execute
(CommandForCursor.java:68)
at com.ibm.j2ca.extension.commandpattern.Interpreter.executeWithChildren
(Interpreter.java:96)
at com.ibm.j2ca.extension.commandpattern.Interpreter.execute(
Interpreter.java:77)
at com.ibm.j2ca.jdbc.JDBCInteraction.executeInternal(
JDBCInteraction.java:358)
at com.ibm.j2ca.jdbc.JDBCInteraction.execute(JDBCInteraction.java:139)
at com.ibm.ws.sca.internal.j2c.J2CMethodBindingImpl.invoke(
J2CMethodBindingImpl.java:242)










WebSphere Process Server 中 SCA 组件事务介绍

因为 WebSphere JDBC Adapter 运行在 WebSphere Process Server (WPS) 之上,其中对数据库事务的控制和管理则是由 WPS 负责完成的,所以必须先了解相关的 WPS 的组件事务知识。

WebSphere Process Server (WPS) 是建立在 WebSphere 应用服务器上的新一代业务集成平台。它支持面向服务的应用架构和企业服务总线,含有符合业界标准的业务流程引擎,使用统一的服务调用和业务表现模型,并遵循业界的开放标准。服务组件架构(Service Component Architecture,SCA)是 WPS 新的编程模型,为其提供了统一的服务调用模型,也提供完整的事务支持。图 3 是 SCA 的组件模型,一个 SCA 组件是由接口、服务实现和引用三个部分构成:


图 3.SCA 组件模型
图 3:SCA 组件模型


其中接口部分表示该组件能提供的业务能力;实现则是实现了该接口的组件;而引用则表示在服务实现中需要调用到的其他服务。在事务方面,SCA 通过在三个部分(接口、服务实现和引用)分别提供不同的限定符(qualifier)的方式来完成事务的定制。


图 4.接口上的事务限定符 
图 4:接口上的事务限定符  


图 4 显示了接口部分的限定符有:Join Transaction。可以取值 true 或者 false。这个限定符表示该组件是否愿意加入到调用方的事务中。


图 5. 实现上的事务限定符
图 5:实现上的事务限定符


图 5 显示了实现部分的限定符有:Transaction。 可以取值 Global、Local 或者 Any。这个限定符表示该组件对运行环境的需求。global 表示需要运行在一个全局事务中,这样 SCA 容器必须保证在调用该服务实现时总是有一个活动的全局事务。local 则表示要运行在本地事务中。容器必须挂起当前活动的全局事务,并负责启动一个本地事务。any 则表示该实现既可以运行在一个已有的全局事务中也可以运行在本地事务中。如果选择了 any,容器如果发现当前没有活动的事务,就会建立一个本地事务。


图 6. 引用上的事务限定符
图 6:引用上的事务限定符


图 6 显示了引用部分的限定符有:Suspend transaction。取值为 true 或者 false。用来表示在调用这个目标服务时是否需要把当前的全局事务挂起(但目标服务是否能加入到这个事务中,还取决于目标服务的事务配置)。如果选择 true,则表示当前服务的事务不会被传播给所调用的服务。这个限定符只针对同步调用有效。

通过这三个部分的组合就可以实现细粒度的事务控制,这样也就能对各个组件实现更为精确的事务控制。下面的表格 1 列举出 Join transaction 和 transaction 两种选项组合的情况下,如何实现对事务的管理和实现。


表 1. 目标组件的事务行为



接口
“Join Transaction”qualifier

实现
“Transaction” qualifier

目标组件的事务行为

True  Global  如果目标组件已有全局事务的上下文环境,那么目标组件就参与到已有全局事务中去;否则,运行时产生一个新的全局事务。 
True  Local  运行时产生一个错误,这个限定符的设置不对。 
True  Any  如果目标组件已有全局事务的上下文环境,那么目标组件就参与到已有全局事务中去;否则,运行时产生一个新的本地事务。 
False  Global  目标组件运行在全局事务中,总是产生一个新的全局事务。 
False  Local  目标组件运行在本地事务中。 
False  Any  目标组件运行在本地事务中。 



下面给一个具体例子,比如一个 SCA 组件有如下的事务需求:在运行时候需要有全局事务但不能加入到已有的全局事务中,该组件需要调用到其他的服务,希望其他的服务也能加入到自己的事务中。那么应该如下设计  :
•接口的事务限定符应该设置为 join transaction=false,这样就防止了该组件加入到上游的事务中;
•实现上的事务限定符应该设置为 transaction=global。这样 SCA 容器在调用该实现之前就会启动一个全局事务;
•引用上的事务限定符应该设置为 suspend transaction=false。这样组件实现的事务就被允许传播到被调用的服务;





WebSphere JDBC Adapter 中 outbound 组件事务介绍

WebSphere JDBC Adapter 的 outbound 组件只能在接口上进行事务限定符的配置,不可以在实现和引用上进行事务限定符的设置。


图 7. WebSphere JDBC Adapter 的 outbound 组件上的事务限定符
图 7:WebSphere JDBC Adapter 的 outbound 组件上的事务限定符


在图 7 中,WebSphere JDBC Adapter 只有接口上的事务限定符的设置,没有提供实现和引用上的事务限定符的设置。从这点上来看,它既符合了 SCA 组件的事务限定符的规则,同时又有它自己的特殊性。

此外,WebSphere JDBC Adapter 提供了三种连接方式,分别是“database URL mode”,“datasource JNDI mode”,“XA datasource name mode”。它们和 WebSphere JDBC Adapter 的 outbound 组件的事务有着很紧密的联系。图 8 中显示了上面三种连接方式。


图 8. WebSphere JDBC Adapter 的连接方式
图 8:WebSphere JDBC Adapter 的连接方式


WebSphere JDBC Adapter 根据接口上的事务限定符和三种连接方式确定 outbound 组件的事务行为,表格 2 中描述了各种组合之下产生的事务行为。


表 2:WebSphere JDBC Adapter 的 outbound 组件的事务行为



接口
“Join Transaction” qualifier

Outbound 组件采用的连接方式

outbound 组件的事务行为

True  1.使用“datasource JNDI mode”连接方式,且 datasource 是非 XA。
2.或者使用“database URL mode”的连接方式。
运行时产生一个错误,这个限定符的设置不对。 
False  1.使用“datasource JNDI mode”连接方式,且 datasource 是非 XA。
2.或者使用“database URL mode”的连接方式。
outbound 组件运行在本地事务中。 
True  1.使用“datasource JNDI mode”连接方式,且 datasource 是 XA。
2.或者使用“XA datasource name mode” 连接方式(仅适用于 DB2,Oracle 数据库)。
如果 outbound 组件已有全局事务的上下文环境,那么 outbound 组件就参与到已有全局事务中去;否则,运行时产生一个新的全局事务。 
False  1.使用“datasource JNDI mode”连接方式,且 datasource 是 XA。
2.或者使用“XA datasource name mode”连接方式(仅适用于 DB2,Oracle 数据库)
outbound 组件运行在本地事务中。 



表格 2 中,当接口上的事务限定符是“join transaction=true”,同时采用“datasource JNDI mode”连接方式,且 WPS 上的 datasource 是 XA 类型的时候,它会在全局事务下运行;当接口上的事务限定符是“join transaction=false”,同时采用“datasource JNDI mode”连接方式,且 WPS 的 datasource 是 XA 类型的时候,它则会在本地事务下运行。

在了解了 WPS 的组件事务和 WebSphere JDBC Adapter 的 outbound 组件知识之后,接下来进一步分析死锁和锁超时是如何产生的。



死锁是如何产生的

WebSphere JDBC Adapter 在并发情况下产生数据库死锁现象,仅仅出现在它采用“datasource JNDI mode”连接方式的时候。在“datasource JNDI mode”连接方式下,WebSphere JDBC Adapter 使用了 WPS 的 Data Source。这个 Data Source 连接数据库的时候,事务隔离级别是被 WPS 的 Data Source 控制的,WebSphere JDBC Adapter 并没有提供对事务隔离级别的控制。在缺省情况下,连接 DB2 和 MSSQLServer 的时候,WPS 上的 Data Source 事务隔离级别在被设置在“TRANSACTION_REPEATABLE_READ”下;在连接 Oracle 的时候,WPS 上的 Data Source 事务隔离级别在被设置在“TRANSACTION_READ_COMMITTED”下。同时,DB2、MSSQLServer、Oracle 数据库端的缺省事务隔离级别都是“TRANSACTION_READ_COMMITTED”。WPS 上的 Data Source 事务隔离级别优先级别高于数据库端的隔离级别,所以当使用 WebSphere JDBC Adapter 的“datasource JNDI mode”连接方式去访问 DB2 和 MSSQLServer 数据库的时候,所产生的数据库连接总是在“TRANSACTION_REPEATABLE_READ”事务隔离级别下,它对事务的一致性和完整性要求比较高,会有死锁现象的发生;但是,访问 Oracle 数据库的时候,所产生的数据库连接则是在“TRANSACTION_READ_COMMITTED”事务隔离级别下,不要求高的事务一致性和完整性,不会有死锁现象的发生。总之,在 WebSphere JDBC Adapter 被并发调用和在较高的事务隔离级别使用的情况下,会对数据库中被访问的表和行产生很多锁,以及出现锁升级的情况,从而导致死锁现象的发生。如果事务隔离级别设置的越高,数据完整性就越高,对并发支持和性能就会降低;如果事务隔离级别设置的越低,对并发支持和性能提高越好,但是数据完整性就会降低。





如何解决死锁问题

要解决死锁问题,可以通过更改数据库连接的事务隔离级别来达到消除死锁现象的发生,具体的更改措施有如下几种。
•更改 WPS 上 Data Source 的缺省事务隔离级别,将缺省的“TRANSACTION_REPEATABLE_READ”事务隔离级别降低到“TRANSACTION_READ_COMMITTED”,详情请查阅“参考资料”一节。
•更改 SCA component 上 EJB 的事务隔离级别,详情请查阅“参考资料”一节。

当 WPS 上的 Data Source 事务隔离级别降低之后,应用程序对并发的支持和性能提高了,从而也就避免数据库死锁现象的发生。





锁超时是怎么产生的

在图 2 场景中,DB2 数据库端的死锁检查时间是 10 秒,锁超时检查时间是 120 秒,也就是说,在数据库端,每过 10 秒中要检查数据库中是否存在死锁现象,每过 2 分钟要检查是否有锁超时现象。在“globaltransaction”outbound 组件中,当它要执行删除主键为“1”的记录的时候,因为同时它已加入到了“BPELGlobalTransaction”组件发起的全局事务中去,所以它必须等待这个 BPEL 组件所有业务逻辑操作全部都执行完毕,这个删除的操作才能最终提交到数据库中去。在没有执行完 BPEL 组件中所有的业务逻辑前,这个删除操作一直不能提交到数据库。这会导致主键为“1”的记录在数据库上被锁住不放。在这个场景中,由于这个“BPELGlobalTransaction”组件执行完成需要 5 分钟的时间,所以数据库会将这条主键为“1”的记录锁住 5 分钟不放直至业务逻辑完成为止。当这条主键为“1”的记录被锁住的同时,第二个“BPELLocalTransaction”组件也启动了,它需要调用“localtransaction”outbound 组件执行创建主键为“1”的记录,此时数据库会检测到这个记录正在被另外一个应用锁住了,所以“BPELLocalTransaction”组件不得不等待在这条记录上锁的释放,过了 2 分钟以后,数据库检查到这个锁仍然没有释放,但是锁超时的时间已经到达,所以 WPS 报告锁超时异常,终止“localtransaction”组件创建记录的操作。通过以上分析,如果 WebSphere JDBC Adapter 产生的 outbound 组件加入到一个全局事务中后,全局事务的时间过长会产生锁超时的异常。





如何解决锁超时问题

在上节的锁超时问题分析中,因为 WebSphere JDBC Adapter 的一个 outbound 组件加入到上游组件的全局事务中去,且很可能这个上游组件的全局事务执行完成所需要的时间超过了数据库锁超时的时间,在这种情况之下,又出现了其他的 WebSphere JDBC Adapter 的 outbound 组件也要访问这个全局事务中已经访问过的记录,此刻就会出现锁超时现象。为了避免这种现象,从设计上就要考虑,应该尽可能的使得全局事务消耗的时间要短,在不同应用中要避免访问相同的表或者记录。当然,适当调整数据库锁超时时间也是可行方法之一。





总结

本文首先引入在 WebSphere JDBC Adapter 上产生死锁和锁超时的场景,然后介绍了 WPS 中 SCA 组件的事务特性,接着论述了 WebSphere JDBC Adapter 的 outbound 组件事务的特别之处,然后在实际应用中,针对事务引发的死锁和锁超时的现象寻找原因和进行分析,最后提供了解决办法去避免死锁和锁超时现象。

你可能感兴趣的:(websphere)