声明:本文思路解决受dbsnake老师指点,特此感谢!
分布式系统较传统的单点系统,具有较强的可拓展性和可用性,是目前大型应用系统普遍采用的设计模式。但是较传统系统,分布式系统无论从复杂性还是出现故障的机率看,都是远远超过单点系统。当我们选择开发分布式系统的时候,就意味要倾注更多的精力。
1、分布式事务
分布式事务是分布式系统的一个重要研究范畴。在Oracle中,采用“二阶段提交”(Two phases submit)作为分布式事务的处理模型。简单的说,当一个事务涉及操作对象分布在不同的数据库上。传统的锁机制和事务模型都是建立在单实例数据库基础上的。分布式事务要保证各个节点上事务都成功的前提下,才能确定事务提交完成。只要有一个事务没有完成,所有节点的事务都要进行rollback。
分布式事务一个最难处理的问题,就是通信带来的timeout取值。分布式事务要伴随着大量的节点间通信交互,确定其他工作节点是否事务是否完成。这种等待是很大的问题,因为节点不知道其他节点不响应是“因为节点已经死亡,还是节点网络繁忙”。所以timeout设置就是一个很大的问题。
这种问题的极端体现就是,一个事务中涉及到多个节点内容。当一个节点锁住一些对象,开启了事务过程,就不断地等待其他节点的响应。而由于各种原因,没有相应到来。这样就需要进行事务回收问题。
通常情况下,Oracle是可以自动处理这种问题的。在一个分布式事务中,有三个层面的timeout参数解决分布式事务锁。
ü 全局事务timeout(Global Transaction Timeout):在这种方式下,Oracle是不负责分布式事务管理的。Oracle将事务管理权交付给应用处理,如JTA事务容器。这种方式的事务管理,完全取决于应用程序配置内容。比如在JDBC和JTA中,有专门的Time out参数;
ü Session Timeout:指定了一个事务可以持续的最长时间,超过了这个时间就会被自动回收中断。一个XA事务与Oracle Process的关系是比较松散,可以进行detach操作。当一个分布式事务失败的时候,Oracle可以通过时间设置情况,将XA事务detach from the process;
ü Oracle内部的distributed_lock_timeout限制住一个分布式事务可以锁住对象的最长时间。该参数通常默认设置一个很大的取值;
上述的三个timeout中,大小配置顺序通常是:global transaction timeout < session timeout < distributed_lock_timeout。
很多时候,配置过大的timeout值,可能会引起一些问题和对象锁定。
2、“shadow session”现象
当我们使用JTA事务模型的时候,分布式事务管理权是从Oracle转移到JTA框架来进行管理。但如果事务中出现JTA的异常终止,就可能出现分布式问题。检查系统中,存在事务信息。
--篇幅原因,结果列有删减;
SQL> select * from v$transaction;
ADDR XIDUSN XIDSLOT XIDSQN UBAFIL
---------------- ---------- ---------- ---------- ----------
070000007B5AC368 1 6 42735 3
存在事务,该事务的标识是(1,6,42735)。此时存在被锁定的对象。
SQL> select * from v$locked_object;
XIDUSN XIDSLOT XIDSQN OBJECT_ID SESSION_ID ORACLE_USERNAME OS_USER_NAME LOCKED_MODE
---------- ---------- ---------- ---------- ---------- --------------------------------- -----------
1 6 42735 114245 181SYSMAN 3
从v$lock_object中,可以看到当前事务锁定object_id=114245的对象,会话编号为sid=181,锁模式为共享锁模式。注意:此时user显示的是sysman的系统内置用户。
此时对象是存在的,也能确定锁对象确实存在。
SQL> select * from dba_objects where object_id=114245;
OWNER OBJECT_NAME
------------------------------ ------------
NBS_COMMON BAT_LOCK
SQL> select * from bat_lock for update nowait;
select * from bat_lock for update nowait
ORA-00054:资源正忙,但指定以NOWAIT方式获取资源,或者超时失效
下面进行sid=181检查,发现该会话是不存在v$session中的。也找不到对应的共享锁信息。
SQL> select count(*) from v$session where sid=181;
COUNT(*)
----------
0
SQL> select * from v$lock where id1=114245;
ADDR KADDR SID TYPE
---------------- ---------------- ---------- ----
最为诡异的情况是v$locked_object中,oracle_username不断的幻象读。
SQL> select * from v$locked_object;
XIDUSN XIDSLOT XIDSQN OBJECT_ID SESSION_ID ORACLE_USERNAME
---------- ---------- ---------- ---------- ---------- ------------------------------
8 12 48826 114245 181
SQL> select * from v$locked_object;
XIDUSN XIDSLOT XIDSQN OBJECT_ID SESSION_ID ORACLE_USERNAME
---------- ---------- ---------- ---------- ---------- ------------------------------
8 12 48826 114245 181 NBS
没有v$session信息就无法进行session级别的kill操作。同时,使用OS级别的Server Process kill操作也没有作用。Oracle始终不能对事务资源进行回收。
3、分析问题和解决
该现象产生的原因是JTA事务的timeout值设置过大造成的。在使用JTA事务模型中,存在一个事务timeout参数,默认值为5分钟。如果使用JTA进行分布式事务,Oracle会将分布式事务的控制权交出,由JTA进行事务控制。
这就是我们在第一部分中说的Global Transaction Timeout取值。JTA Timeout变相定义了在使用JTA中,一个数据库操作事务可以持续多长时间。如果超过这个时间,JTA会自动中断正在执行的事务,并且进行回滚。这也就限制了一个事务的时间长短。
对OLTP系统来说,我们一般期望事务规模尽可能小且短,时间越快越好。从而保证并发性和可拓展性。对OLAP系统来说,就有所差别。一些SQL执行过程可能会较长。所以对不同系统设置不同的JTA Timeout值,是必要的。
JTA是会向Oracle抢夺事务控制权的。如果JTA在一个分布式事务进行中崩溃,那么Oracle是不会对事务进行资源回收和锁释放的。
在我们的例子中,就是一个明显的表现。Oracle只有经过JTA Timeout设置的时间,才可能会进行回收。
那么,有没有办法在JTA失败后,不经过Timeout时间进行回收呢?在MOS 1248848.1中,我们找到了Java代码XA_rb.java,执行这个类方法,就可以实现资源的回收。代码地址:http://space.itpub.net/17203031/viewspace-709216。
该程序的原理,就是使用Oracle JDBC实现中的类,对特定事务进行强制回收。作为参数,要输入连接数据库的URL地址,以及回收事务的标识。
++ 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 !
此时,再进行检查,可以发现事务被回收。
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
SQL> rollback;
Rollback complete
4、结论
分布式事务较之传统事务模型,复杂性和管理难度要远远大于传统事务。Oracle内部支持分布式事务,有二阶段提交模型。一些事务管理框架,也都存在相应的事务模型方法。
对分布式事务中存在的问题,需要重点关注。