首批通过分布式安全可靠测评,为关键业务系统打造
通过脚本检查并回滚悬挂 XA 事务
更新时间:2026-06-10 09:51
本文介绍如何通过脚本检查悬挂 XA 事务,并进行回滚。
适用版本
OceanBase 数据库 V2.2.7X 版本
检查并回滚悬挂 XA 事务
目前,OceanBase 数据库中,当某个 RM(Resource Manager,资源管理器) 提交异常时,TM(Transaction Manager,事务管理器)未正常发送 Rollback 到 OceanBase 数据库,导致数据库端的 XA 事务一直处于 Prepare 阶段,并最终导致 Clog 无法回收,合并异常。
可以通过在任意一台 OBServer 上运行以下 Python 脚本,完成悬挂 XA 事务的自动巡检。 其中 interval 480 second表示回滚超过 8 分钟的事务,ROUND((sysdate - cast(GMT_MODIFIED as date)) * 86400) > 600 表示每 5 分钟进行一次巡检。
import subprocess
import logging
import time
import os
logging.basicConfig(filename='xa_trans_rollback.log', level=logging.INFO, filemode='a')
rollback_xa_cmd='''
DECLARE
CURSOR cur
IS
SELECT GMT_MODIFIED,
state,
rawtohex(GTRID) gtrid,
rawtohex(BQUAL) bqual,
FORMAT_ID
FROM sys.all_virtual_tenant_global_transaction_agent
WHERE state = 3
AND ROUND((sysdate - cast(GMT_MODIFIED as date)) * 86400) > 600;
l_xid DBMS_XA_XID;
l_ret PLS_INTEGER;
BEGIN
FOR rec IN cur
LOOP
l_xid.formatid := rec.format_id;
l_xid.gtrid := hextoraw(rec.gtrid);
l_xid.bqual := hextoraw(rec.bqual);
l_ret := DBMS_XA.XA_ROLLBACK(xid => l_xid);
dbms_output.put_line(l_ret);
END LOOP;
END;
/
'''
while True:
try:
cur_time = time.strftime("%Y-%m-%d %H:%M:%S",time.localtime())
pending_trans_cmd = "mysql -hxxx.xx.xxx.xx -Pxxxx -uroot@sys#$cluster -p'$passwd' -c -Doceanbase -e 'select count(*) from __all_virtual_trans_stat where tenant_id=$tenant_id and part_trans_action > 2 and ctx_create_time < date_sub(now(),interval 480 second);'"
p = subprocess.Popen(pending_trans_cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE,shell=True)
pending_trans_cnt= p.communicate()[0].replace('count(*)','').strip("\n")
if int(pending_trans_cnt) > 0:
pending_xa_rollback = "obclient -hxxx.xx.xxx.xx -P3306 -usys@obzy1#$cluster -p'$passwd' -e '" + rollback_xa_cmd + "'"
os.system(pending_xa_rollback)
logging.info(cur_time + ': ' + pending_trans_cnt + ' pending trans were rollbacked!')
else:
logging.info(cur_time + ": No pending trans found!")
time.sleep(300)
except:
cur_time = time.strftime("%Y-%m-%d %H:%M:%S",time.localtime())
logging.error(cur_time + ": Error, get pending trans failed!")
time.sleep(300)