ORACLE悬疑分布式事务问题处理

当须要在多个Oracle数据库之间进行数据一致性操做时,就会用到分布式事务。
例如:html

insert into T_log@remote_db;  --远程数据库插入
insert into T_local;          --本地数据库插入
commit;

分布在本地和远程两个db的事务同时操做,这就构成了一个分布式事务。git

分布式事务采用Two-Phase Commit提交机制,保证分布在各个节点的子事务可以所有提交或所有回滚的原子性。在这种机制下,事务处理过程分为三个阶段:github

  1. PREPARE:发起分布式事务的节点通知各个关联节点准备提交或回滚。各关联节点此时会作三个事情:刷新redo信息到redo log中;将持有的锁转换为悬疑事务锁;取各节点中最大的SCN号进行同步
  2. COMMIT:写入commited SCN,释放锁资源
  3. FORGET:悬疑事务表和关联的数据库视图信息清理

因为分布式事务涉及到多个数据库之间进行操做,偶尔会遇到一些异常状况(例如系统或网络中断)致使上述三个阶段出现异常,这就在一个或多个节点上,产生不完整的“悬疑分布式事务”。
大多数状况下,出现这种问题,Oracle会由Reco进程进行自动修复,Oracle数据库会在dba_2pc_pending 和dba_2pc_neighbors等多个视图中记录分布式事务相关的信息,事实上reco进程也是基于这些信息去作自动修复的。
Reco进程会尝试链接到其余节点获取分布式事务信息,而后尝试修复失败的事务,并将对应的事务中的记录删除。
但有些状况下(例如节点没法正常访问或事务表中记录的数据不完整),Reco进程不能正常完成这个工做,就会抛出异常。对于分布式事务,对应的异常代码区间是ORA-02040 - ORA-02099,可经过alert日志查看到错误信息。
例如:数据库

ORA-02054: transaction in-doubt
The transaction is neither committed or rolled back locally, and we have lost communication with the global coordinator.

此时每每须要手工处理进行干预。网络

如下是三种常见的分布式事务问题场景:session

  1. dba_2pc视图中有数据,但分布式事务已经不存在
  2. 分布式事务存在,但dba_2pc视图中没有数据
  3. 事务和视图数据都有,可是执行commit force或rollback force时hang住

经过报错会有提示,例如:oracle

ORA-01591: lock held by in-doubt distributed transaction 10.20.360
这个10.20.360就是咱们须要检查分布式事务ID

场景一:dba_2pc视图中有数据,但分布式事务已经不存在
视图有数据,那么先检查数据的状态
分布式

select * from dba_2pc_pending where local_tran_id='10.20.360';

主要看state字段。
若是事务已是committed, rollback forced或者commit forced状态,表示事务已经完成了,可是在FORGET阶段处理时,数据库字典的信息没能及时清除。此时,咱们调用oracle的清理丢失事务信息的语句就能够完成处理:post

execute DBMS_TRANSACTION.PURGE_LOST_DB_ENTRY ('10.20.360');

若是事务是PREPARED状态,可是在事务表中又没有活动的事务:this

SELECT KTUXEUSN, KTUXESLT, KTUXESQN, /* Transaction ID */
KTUXESTA Status,
KTUXECFL Flags
FROM x$ktuxe
WHERE ktuxesta!='INACTIVE'
AND ktuxeusn= 10;   --注意替换这里的回滚段号(xid=usn.slot.(sqn+1))
----没有活动的事务

那此时须要手工清理丢失事务的信息

set transaction use rollback segment SYSTEM;
delete from sys.pending_trans$ where local_tran_id = ;
delete from sys.pending_sessions$ where local_tran_id = ;
delete from sys.pending_sub_sessions$ where local_tran_id = ;
commit;

场景二:分布式事务存在,但dba_2pc视图中没有数据

遇到ORA-2054, ORA-1591等错误,检查dba_2pc视图没有记录,这种场景不常见,只在少数极端的状况下出现。
先确认现象,分别检查x$ktuxe和 dba_2pc_pending视图,查询语句与场景一相同
在这种状况下不管是执行commit force仍是rollback force,都会直接抛出异常:

 commit force '10.20.360';
 ORA-02058: no prepared transaction found with ID 10.20.360

这时咱们须要将视图对应的基表数据补入,而后再执行rollback force。

  alter system disable distributed recovery;
  insert into pending_trans$ (
    LOCAL_TRAN_ID, GLOBAL_TRAN_FMT,
    GLOBAL_ORACLE_ID,STATE, STATUS,
    SESSION_VECTOR,RECO_VECTOR,TYPE#, FAIL_TIME, RECO_TIME)
    values( '10.20.360', /* <== Replace this with your local tran id */
    306206, 'xxxxxxxx.00000.0.0.0', 
    'prepared','P', 
    hextoraw( '00000001' ),hextoraw( '00000000' ), 0, sysdate, sysdate );
  insert into pending_sessions$ 
    values( '10.20.360',1, hextoraw('00000000'), 'C', 0, 1433927502, '', 14);
    --1433927502为DBID, 14为userid
  commit;
  rollback force '10.20.360';
  EXECUTE DBMS_TRANSACTION.PURGE_LOST_DB_ENTRY('10.20.360'); 
  --手工清理事务信息

场景三:事务和视图数据都有,可是执行commit force或rollback force时hang住

若是视图和事务表中都有数据,并且状态是PREPARED,先执行commit force或rollback force,一般就能解决问题,但有时候也会遇到执行force处理时hang住
尝试purge事务信息时,有提示报错:

BEGIN DBMS_TRANSACTION.PURGE_LOST_DB_ENTRY('10.20.360'); END; 
* 
ERROR at line 1: 
ORA-06510: PL/SQL: unhandled user-defined exception 
ORA-06512: at "SYS.DBMS_TRANSACTION", line 94 
ORA-06512: at line 1

此时须要进行场景一和场景二的结合起来的全部步骤:

1. 先将视图对应的基表数据删除
   delete from sys.pending_trans$ where local_tran_id = '10.20.360'; 
   delete from sys.pending_sessions$ where local_tran_id = '10.20.360'; 
   delete from sys.pending_sub_sessions$ where local_tran_id ='10.20.360'; 
   commit;
2. 再插入pending_trans$和pending_sessions$数据,见场景二
3. rollback force '10.20.360'; 
4. Purge the transaction: 
   exec dbms_transaction.purge_lost_db_entry('10.20.360');

 

完整实例:http://github.crmeb.net/u/defu

来自 “开源世界 ” ,连接:http://ym.baisou.ltd/post/645.html,如需转载,请注明出处,不然将追究法律责任。