天天看点

ORA-01591: lock held by in-doubt distributed transaction 20.25.280352

客户一套oracle11.2.0.4rac一体机,应用侧抛来一个报错

ORA-01591: lock held by in-doubt distributed transaction 20.25.280352

最开始看上去像是事务锁,但是问客户知道这是查的哪张表,客户反馈知道,就通过一般性事务去查了该表的锁:

SELECT lpad(' ',decode(l.xidusn ,0,3,0))||l.oracle_username User_name,

o.owner,o.object_name,o.object_type,s.sid,s.serial#

FROM v$locked_object l,dba_objects o,v$session s

WHERE l.object_id=o.object_id

AND l.session_id=s.sid 

AND upper(o.OBJECT_NAME) LIKE '%TO_ALERT_SMS_%'

ORDER BY o.object_id,xidusn DESC ;

发现该语句没有任何返回,意思到这可能不是一般的事务锁。

搜索该问题,发现有篇文章

在Oracle中,分布式事务ORA-01591错误如何解决? - 云+社区 - 腾讯云

里面也是这种报错,是一种分布式事务2节点提交锁,大意就是,通过dblink连接另一个数据库的表是,对另一个数据库的表进行dml操作,同时也可能在同一个事务中对本库中的本地表做事务提交操作,那么对于dblink的表,需要确保远端的数据库也要提交,那么在本地像远端确认提交的时候,先由本地库发一个prepare的确认信息,远端回复prepare了以后,本地在执行commit信息;此种情况就是在本地执行commit以后,此时远端库还没有完全commit,此时网络中断,导致本地库该表是commit状态,但是远端库还是prepare状态,一般来讲,此时如果源端得到的事务信息是全的,那么数据库不需要人为干预,能够自动根据事务信息进行提交操作,但是出现这个问题的时候,基本就表示提交的事务信息不足以让远端库执行提交操作,所以此时这种事务就需要人为干预了。

对于这样的事务,在运气好的情况下,对于这个事务,只能通过连接网络或者强制提交回退事务来结束。可以使用COMMIT FORCE或者ROLLBACK FORCE来进行处理,在这里,进行回滚操作,如下所示:

ROLLBACK FORCE '20.25.280352';
      

如果能执行过去,那么该

而我这次情况下,执行这条语句会夯,客户反馈执行了8个小时,命令还未返回。

恩墨写了一篇文章,可以很好的解决这个问题

​​​​​​ORACLE悬疑分布式事务问题处理 - 墨天轮

1、查询dba_2pc_pending表有数据,那么要看数据的状态

select * from dba_2pc_pending where local_tran_id='20.25.280352';

ORA-01591: lock held by in-doubt distributed transaction 20.25.280352

我这个表有这个事务的信息,而且状态还是prepared的状态。

主要看state字段。

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

execute DBMS_TRANSACTION.PURGE_LOST_DB_ENTRY ('20.25.280352');

 很遗憾,我们不是这种情况,我们是prepared的状态,继续下面的核查。

2、如果事务是PREPARED状态,就需要在事务表中看是否有活动的事务,如果下面的查询没有返回值,则说明活动表中没有活动的事务,那么可以直接执行下面的语句:

SELECT ktuxeusn,
ktuxeslt,
ktuxesqn,
ktuxesta status,
ktuxecfl flags
FROM x$ktuxe
WHERE ktuxesta != 'inactive'
AND ktuxeusn = 20
AND ktuxeslt = 25
AND ktuxesqn = 280352 ;
           

查询条件就是返回的事务锁号"20.25.280352"拆开即可。如果没有数据,则可以直接执行下面的操作手工清理丢失的事务信息:

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

很遗憾,我们查询也是有活动的事务的

ORA-01591: lock held by in-doubt distributed transaction 20.25.280352

3、如果视图和事务表中都有数据,而且状态是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
           

 我这边执行也确实报错了

ORA-01591: lock held by in-doubt distributed transaction 20.25.280352

此时需要做如下操作:

删除对应分布式事务信息,

delete from sys.pending_trans$ where local_tran_id = '20.25.280352';

delete from sys.pending_sessions$ where local_tran_id = '20.25.280352';

delete from sys.pending_sub_sessions$ where local_tran_id ='20.25.280352';

commit;
           

 禁用自动分布式恢复

alter system disable distributed recovery;
           

再次插入对应事务id的分布式信息,下面两条语句除了local_tran_id需要更改成你实际情况的事务id,其他信息可以跟我一样,原封不动的插入进去即可,因为该信息本身就是随意提供的,只是为了后面回滚使用。 

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     ( '20.25.280352', /* <== Replace this with your local tran id */
             306206,
             '123.12345.1.2.3',
             'prepared',
             'p',
             Hextoraw('00000001'),
             Hextoraw('00000000'),
             0,
             sysdate,
             sysdate );

INSERT INTO pending_sessions$
VALUES     ( '20.25.280352',/* <==replace only this with your local tran id */
             1,
             Hextoraw('05004f003a1500000104'),
             'c',
             0,
             30258592,
             '',
             146 );
             
             
commit ;
           

回滚事务

rollback force '20.25.280352';
           

重新启用自动分布式事务恢复

alter system enable distributed recovery;
           

此时在pending表中查看我们刚才插入的信息,会发现state状态字段会变成force rollback状态了。

ORA-01591: lock held by in-doubt distributed transaction 20.25.280352

清理没用的事务信息

exec dbms_transaction.purge_lost_db_entry('20.25.280352');
           

 此步做完,下面的pending表就差不到回滚的事务信息了,此时表的分布式事务就解除了,表就能正常使用了。

select * from dba_2pc_pending where local_tran_id='20.25.280352';

后记:还有一种情况,我们本次未遇到,就是分布式事务存在,但dba_2pc(pending表)视图中没有数据

遇到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;
           

插入pending表事务信息

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';
           

 重新启用分布式事务自动恢复

alter system enable distributed recovery;
           

手工清理分布式事务陈旧信息

EXECUTE DBMS_TRANSACTION.PURGE_LOST_DB_ENTRY('10.20.360');