一种诡异的由于JTA分布式事务异常导致的锁的解锁过程
Posted: December 12, 2011 | Author: Cui Hua | Filed under: Oracle | Tags: XA Transaction Rollback | Leave a comment »这个问题曾困扰了我很久,直到有一天我偶然间在MOS上看了一篇文档“Solving locking problems in a XA environment [ID 1248848.1]”,这里面提到了在XA环境下有一段java代码可以用来rollback由于JTA分布式事务异常所导致的锁,我试了之后,发现果然可以解决上述问题。我这里记录下整个过程,也许能对朋友们有所帮助。
这个问题是这样的:
库里存在一个transaction:
SQL> select * from v$transaction;
ADDR XIDUSN XIDSLOT XIDSQN UBAFIL
—————- ———- ———- ———- ———-
070000007B5AC368 1 6 42735 3
v$locked_object的查询结果告诉我们上述transaction锁住了object id为114245的对象,且该transaction是由session id为181的session所产生,这个session所在的oracle_username为sysman:
SQL> select * from v$locked_object;
XIDUSN XIDSLOT XIDSQN OBJECT_ID SESSION_ID ORACLE_USERNAME OS_USER_NAME LOCKED_MODE
———- ———- ———- ———- ———- ——————————— ———–
1 6 42735 114245 181 SYSMAN 3
此时object id为114245的对象是存在的:
SQL> select * from dba_objects where object_id=114245;
OWNER OBJECT_NAME
—————————— ————
NBS_COMMON BAT_LOCK
此时表bat_lock确实也被锁住了:
SQL> select * from bat_lock for update nowait;
select * from bat_lock for update nowait
ORA-00054: 资源正忙, 但指定以 NOWAIT 方式获取资源, 或者超时失效
但诡异的是sid为181的session在v$session中是不存在的:
SQL> select count(*) from v$session where sid=181;
COUNT(*)
———-
0
且v$lock里也不存在相对应的锁:
SQL> select * from v$lock where id1=114245;
ADDR KADDR SID TYPE
—————- —————- ———- —-
更为诡异的是v$locked_object中,oracle_username在不断的变化,刚才显示的oracle_username为sysman,现在再查询的时候已经变成了空,过一会儿再查又变回来了:
SQL> select * from v$locked_object;
XIDUSN XIDSLOT XIDSQN OBJECT_ID SESSION_ID ORACLE_USERNAME
———- ———- ———- ———- ———- ——————————
8 12 48826 114245 181
当我第一次碰到上述问题的时候,试图从数据库端rollback上述transaction,但在我不重启库的情况下发现无论如何也不行。
后来我们发现了一个规律——上述transaction的rollback是受限于JTA的timeout设置,于是当时采用的临时的解决方法就是把JTA的timeout值调小,这样,虽然我在数据库端对上述transaction无能为力,但是一旦达到了JTA的timeout时限,上述transaction就自动rollback了。
好了,我们现在来看看“Solving locking problems in a XA environment [ID 1248848.1]”里针对上述问题的正解:
其核心是利用了一小段java代码XA_rb.java。
XA_rb.java的源代码在这里:
XA_rb.java中提供的方法流程是:
1、首先手工查询出异常的transaction造成的锁的XIDUSN,XIDSLT,XIDSQN值(通过查询v$lock);
2、在java客户端创建一个XA的connection。使用上面的值作为输入参数,执行下面的sql:
select g.K2GTIFMT, g.K2GTITID_EXT, g.K2GTIBID, rawtohex( g.K2GTITID_EXT), rawtohex(g.K2GTIBID) from sys.v_x$k2gte g, sys.v_x$ktcxb t, sys.v_x$ktuxe x where g.K2GTDXCB =t.KTCXBXBA and x.KTUXEUSN = t.KXIDUSN(+) and x.KTUXESLT = t.KXIDSLT(+) and x.KTUXESQN =t.KXIDSQN(+) and t.KXIDUSN=XXX and t.kXIDSLT=XXX and t.kXIDSQN= XXX;
3、使用上面的查询结果中的前三项,formatId, global transacrion Id , branch qualifier,在java客户端创建一个OracleXid对象;
4、通过XA的connection提供的rollback方法,对这个OracleXid对象做rollback。
我们来看一下针对刚才那个XA lock的实际执行过程:
java XA_rb jdbc:oracle:thin:@10.1.15.64:1521:NBSDEV -ROLLBACK 1 6 42735
++ URL: jdbc:oracle:thin:@10.1.15.64:1521:NBSDEV
++ Rollback of Local_tran_ID : 1.6.42735
++ got XA resource handle
++ got Connection handle
++ SQL : select g.K2GTIFMT, g.K2GTITID_EXT, g.K2GTIBID, rawtohex( g.K2GTITID_EXT), rawtohex(g.K2GTIBID) from sys.v_x$k2gte g, sys.v_x$ktcxb t, sys.v_x$ktuxe x where g.K2GTDXCB =t.KTCXBXBA and x.KTUXEUSN = t.KXIDUSN(+) and x.KTUXESLT = t.KXIDSLT(+) and x.KTUXESQN =t.KXIDSQN(+) and t.KXIDUSN=1 and t.kXIDSLT=6 and t.kXIDSQN= 42735
++ Getting XID for Local_Tran_ID: 1.6.42735
+ Found global XID: –>
++ Format Id: 1096044365
++ Group Id: 31302E312E362E3233392E746D30303030313031303435
++ Branch Id: 31302E312E362E3233392E746D31
-> Would your really like to continue (yes or no):
yes
… doing Rollback
… Rollback done !
此时再进行检查,可以看到上述transaction已经被rollback,困扰了我们很久的XA lock也终于不复存在:
SQL> select * from v$locked_object;
XIDUSN XIDSLOT XIDSQN OBJECT_ID SESSION_ID ORACLE_USERNAME
———- ———- ———- ———- ———- ———————
SQL> select * from bat_lock for update nowait;
LOCK_NAME
——————————
TASK_ACCESS
这似乎正应验了一句俗语——解铃还须系铃人🙂
Recent Comments