一种诡异的由于JTA分布式事务异常导致的锁的解锁过程

这个问题曾困扰了我很久,直到有一天我偶然间在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 id114245的对象,且该transaction是由session id181session所产生,这个session所在的oracle_usernamesysman

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 id114245的对象是存在的:

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 方式获取资源, 或者超时失效

 

但诡异的是sid181sessionv$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_usernamesysman,现在再查询的时候已经变成了空,过一会儿再查又变回来了:

SQL> select * from v$locked_object;

 

    XIDUSN    XIDSLOT     XIDSQN  OBJECT_ID SESSION_ID ORACLE_USERNAME              

———- ———- ———- ———- ———- ——————————

         8         12      48826     114245        181                               

 

当我第一次碰到上述问题的时候,试图从数据库端rollback上述transaction,但在我不重启库的情况下发现无论如何也不行。

后来我们发现了一个规律——上述transactionrollback是受限于JTAtimeout设置,于是当时采用的临时的解决方法就是把JTAtimeout值调小,这样,虽然我在数据库端对上述transaction无能为力,但是一旦达到了JTAtimeout时限,上述transaction就自动rollback了。

 

好了,我们现在来看看“Solving locking problems in a XA environment [ID 1248848.1]”里针对上述问题的正解:

其核心是利用了一小段java代码XA_rb.java

XA_rb.java的源代码在这里:

 XA_rb

 

XA_rb.java中提供的方法流程是:

1、首先手工查询出异常的transaction造成的锁的XIDUSNXIDSLTXIDSQN值(通过查询v$lock);

2、在java客户端创建一个XAconnection。使用上面的值作为输入参数,执行下面的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、通过XAconnection提供的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

 

这似乎正应验了一句俗语——解铃还须系铃人🙂