1、问题现象
但是实际情况,完全出乎笔者的想法。检查一般对象数据表锁定,只需要检查v$locked_object和v$transaction视图,就可以定位到具体人。但是检查之后的结果如下:
SQL> select * from v$locked_object;
XIDUSN XIDSLOT XIDSQN OBJECT_ID SESSION_ID
---------- ---------- ---------- ---------- ----------
SQL> select * from v$transaction;
ADDR XIDUSN XIDSLOT XIDSQN
---------------- ---------- ---------- ----------
两个关键视图中,没有锁定的对象,也没有正在进行没有提交的事务。那是不是没有锁定呢?尝试对数据表加锁。
SQL> select * from nbs_common.inc_inputlog for update;
select * from nbs_common.inc_inputlog for update
ORA-01591:锁被未决分布式事务处理4.30.31555持有
SQL> select count(*) from nbs_common.inc_inputlog;
COUNT(*)
----------
426
系统没有像一般阻塞那样等待,而是报错Ora-01591。并且提示锁被一个分布式事务持有,不能实现加锁操作。
看来是一个没有见到过的新错误。
2、分析问题
Ora-01591错误究竟是什么呢?我们使用oerr工具查看该错误编号,看看有没有值得关注的信息。
[oracle@bspdev ~]$ oerr ora 01591
01591, 00000, "lock held by in-doubt distributed transaction %s"
// *Cause: Trying to access resource that is locked by a dead two-phase commit
// transaction that is in prepared state.
// *Action: DBA should query the pending_trans$ and related tables, and attempt
// to repair network connection(s) to coordinator and commit point.
// If timely repair is not possible, DBA should contact DBA at commit
// point if known or end user for correct outcome, or use heuristic
// default if given to issue a heuristic commit or abort command to
// finalize the local portion of the distributed transaction.
简单的说,01591错误的原因是该对象被一个处在“in-doubt”状态的分布式事务锁定。分布式事务使用的是“two-phase commit”二阶段提交技术。解决该问题的方法就是查看内部表pending_trans$,确定分布式事务信息。这种状态的事务主要是由于在进行分布式事务时候,发生网络突发中断的情况,引起分布式事务无法正常结束,等待中断节点的事务响应。于是,各节点的事务所锁定的表就不会被释放掉。
此时,我们检查视图DBA_2PC_PENDING(或者基表pending_trans$),查看是否存在这种情况。
SQL> select * from DBA_2PC_PENDING;
LOCAL_TRAN_ID GLOBAL_TRAN_ID STATE
---------------------- ----------------------------------------------------- ----------------
4.30.31555 096044365.31302E312E33392E38392E746D30303034313030303237 Pepared
(篇幅原因,有省略……)
果然,当前存在一个阻塞分布式事务,处在prepared状态。
3、知识介绍
现代数据库系统往往伴随着复杂的结构和环境,其中分布式数据库组成是一个重要方面。系统后台的数据库系统不再是由单个数据构成,而是由多台独立数据库、甚至是多台异构数据库构成。这种情况下,分布式事务就是开发设计人员不能不面对的一个难题。
处理分布式事务的方案,有两种趋势。其一是借助数据库自身的分布式处理能力,如Oracle的分布式二阶段提交模型,进行多个数据库的分布式事务同步。其二是将分布式事务处理权交付给应用中间层,让应用去处理分布式事务问题。
进行分布式事务的时候,使用的“二阶段提交”模型,大致分为几个过程。(参考:
http://blog.itpub.net/post/38439/477038)
ü Prepare阶段:多个数据库的commit_point_strength进行比较,确定出一个数据库作为commit point site。由全局协调者(Global Coordinator)通知除了commit point site外所有节点准备好commit和rollback。同时,各节点对事务相关数据表加锁。之后,各个节点通知全局协调者自己的SCN,选择最大的那个SCN作为当前事务的SCN;注意,从此刻开始,除了commit point site外,其他节点均进入in_double状态;
ü Commit阶段:全局协调者将确定好的最大SCN通知给commit point site,授权该节点进行commit操作。Commit point site进行事务commit/rollback之后,通知全局协调者事务完成。全局协调者通知其他所有节点进行commit操作;
ü Forget阶段:当各个节点结束事务之后,通知commit point site当前事务已经完成。当全部都完成了,commit point site开始清理分布式事务信息,然后通知全局协调者清理信息。最后全局协调者将最后清理掉本地的事务信息;
当前问题,主要是源于在进入prepared阶段之后,发生了网络中断的现象,引起commit的阶段不能等待到事务信息。所以,才会一直处在Prepared状态,数据表也就不会进行释放。
4、问题解决
对于这个事务,只能通过连接网络或者强制提交回退事务来结束。我们可以使用commit force或者rollback force来进行处理。
SQL> rollback force '4.30.31555';
Rollback complete
Rollback force参数是DBA_2PC_PENDING中记录本地事务信息的编号。
此时,再次查看数据。
SQL> select * from DBA_2PC_PENDING;
LOCAL_TRAN_ID GLOBAL_TRAN_ID STATE
---------------------- ----------------------------------------------------------
4.30.31555 096044365.31302E312E33392E38392E746D30303034313030303237forced rollback
此时,该事务状态已经变化为forced rollback。已经强制回退。
SQL> select seq_number from nbs_common.inc_inputlog where rownum<2 for update;
SEQ_NUMBER
--------------
2
5、结论
这个故障解决,使我获取到如下认识:
ü 系统一旦涉及到分布式数据库,整体的复杂性就要提升很多。所以,要对分布式事务处理技术有非常成熟的认识和理解,而且要经过严格的测试;
ü 锁表的现象多种,不同事务类型,查看信息的方式有所差异;