客户一套oracle11.2.0.4rac一体机,应用侧抛来一个报错
最开始看上去像是事务锁,但是问客户知道这是查的哪张表,客户反馈知道,就通过一般性事务去查了该表的锁:
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';
我这个表有这个事务的信息,而且状态还是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;
很遗憾,我们查询也是有活动的事务的
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
我这边执行也确实报错了
此时需要做如下操作:
删除对应分布式事务信息,
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状态了。
清理没用的事务信息
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');