ORACLE 11G 分布式事务问题处理-ORA-01591

来源:这里教程网 时间:2026-03-03 23:07:22 作者:

环境:Oracle  11G 单实例现象:查询业务表通过order by 会报如下错误ORA-01591: lock held by in-doubt distributed transaction 9.24.1205714原理:分布在本地和远程两个db的事务同时操作,这就构成了一个分布式事务。分布式事务采用Two-Phase Commit提交机制,保证分布在各个节点的子事务能够全部提交或全部回滚的原子性。在这种机制下,事务处理过程分为三个阶段:

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

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

    dba_2pc视图中有数据,但分布式事务已经不存在 分布式事务存在,但dba_2pc视图中没有数据

    事务和视图数据都有,但是执行commit force或rollback force时hang住

处理过程:通过查询视图:GV$TRANSACTION, DBA_2PC_PENDING 为空,DBA_2PC_NEIGHBORS  中有内容,信息如下: 强制清理:

-- 方式1:回滚该分布式事务(推荐,若不确定事务结果)
ROLLBACK FORCE '9.24.1205714';
-- 方式2:提交该分布式事务(仅确认事务需提交时使用)
COMMIT FORCE '9.24.1205714';
-- 若上述命令仍无法解决,可清理pending事务(需谨慎)
EXECUTE DBMS_TRANSACTION.PURGE_LOST_DB_ENTRY('9.24.1205714');
-- 清理所有挂起事务
DBMS_TRANSACTION.PURGE_MIXED('*');
-- 清理所有事务
DBMS_DEFER_SYS.PURGE;

报错如下:ORA-06510: PL/SQL: unhandled user-defined exception ORA-06512: at "SYS.DBMS_TRANSACTION", line 89 ORA-06512: at line 1 场景一:dba_2pc视图中有数据,但分布式事务已经不存在视图有数据,那么先检查数据的状态

select * from dba_2pc_pending where local_tran_id='9.24.1205714';

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

execute DBMS_TRANSACTION.PURGE_LOST_DB_ENTRY ('10.20.360');

如果事务是PREPARED状态,但是在事务表中又没有活动的事务:

SELECT KTUXEUSN, KTUXESLT, KTUXESQN, /* Transaction ID */KTUXESTA Status,
KTUXECFL FlagsFROM x$ktuxeWHERE 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 '9.24.1205714';
 ORA-02058: no prepared transaction found with ID 9.24.1205714

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

/* Formatted on 2023/1/15 11:09:07 (QP5 v5.163.1008.3004) */
  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 ('9.24.1205714',       /* <== 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 ('9.24.1205714',             1,
             HEXTORAW ('00000000'),             'C',             0,             1433927502,             '',             14);    --1433927502为DBID, 14为useridCOMMIT;ROLLBACK FORCE '9.24.1205714';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('9.24.1205714'); 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 = '9.24.1205714'; 
   delete from sys.pending_sessions$ where local_tran_id = '9.24.1205714'; 
   delete from sys.pending_sub_sessions$ where local_tran_id ='9.24.1205714'; 
   commit;2. 再插入pending_trans$和pending_sessions$数据,见场景二3. rollback force '9.24.1205714'; 
4. Purge the transaction: 
   exec dbms_transaction.purge_lost_db_entry('9.24.1205714');

相关推荐