聊聊Oceanbase的悬挂事务 suspend_transaction
OceanBase 数据库的事务按照参与者的个数和位置可以分为三种类型:普通事务(单机事务)、分布式事务、XA 事务;普通事务、分布式事务又可以统称为非 XA 事务,当 OceanBase 为 V4.0 及以上版本时不支持 XA 事务类型,XA事务是分布式事务的一种标准实现方式,使用两阶段提交(2PC)协议,而分布式事务实现方式多样,不限于XA协议,还有Paxos协议和的Raft协议,一些现代分布式数据库(如 Google Spanner)结合了 2PC(类似XA) + Paxos,2PC 用于跨分片事务提交,Paxos 用于副本数据同步,确保高可用,Oceanbase同理。
而事务按照执行的时间和状态可以分为长事务、悬挂事务、其他事务 三种。其中长事务和悬挂事务会导致资源长时间不释放,等待会话长时间被阻塞,可能会占用数据库资源,并对后续的事务处理产生影响。需要重点关注这类异常的事务。
- 悬挂事务是指客户端与 OBProxy 之间已经断开,但 OBProxy 与数据库端的链接仍然存在,事务已经进入提交阶段(事务阶段主要有包含初始化、prepare、SQL执行、Commit、Clear),但是由于某种原因,导致事务无法提交成功, 这类事务处于未完成(COMMIT)/回滚(ROLLBACK)状态。
- 长事务是指链路连接正常,事务开始后,执行时间超过xx秒配置时间阀值的事务。
- 其他事务:除长事务及悬挂事务之外的为其他事务。
悬挂事务的原因
悬挂事务可能由以下几种情况引起:
- 应用程序异常:如果应用程序在执行事务的过程中出现异常崩溃,未能妥善处理事务的提交或回滚,就会导致事务悬挂。
- 长时间运行的事务:某些事务可能由于业务逻辑复杂或数据量庞大,执行时间过长,而未能在预定时间内完成。
- 锁竞争:在并发环境中,多个事务可能因为争夺同一资源的锁而陷入死锁状态,导致事务无法继续执行直至超时或手动干预。
- 网络故障:网络不稳定或断开也可能导致事务请求未能成功到达数据库服务器,进而造成事务悬挂。
- 资源耗尽:如果系统资源(如内存、CPU、磁盘空间等)不足,可能导致事务无法完成。
悬挂事务的影响
悬挂事务可能对数据库系统产生以下负面影响:
- 资源消耗:悬挂事务会占用数据库的连接、锁等资源,影响系统的整体性能。
- 数据一致性问题:如果悬挂事务持有某些数据的锁,其他事务可能无法访问这部分数据,从而导致数据一致性问题。
- 死锁:悬挂事务可能导致其他事务在等待资源释放时陷入死锁状态。
- 性能下降:悬挂事务的存在可能导致系统响应时间延长,影响用户体验。
处理悬挂事务的方法
为了处理悬挂事务,可以采取以下措施:
- 超时设置:设置合理的事务超时时间,当事务运行时间超过设定值时自动回滚。
- 定期检查:定期检查数据库中的活动事务,查找并处理悬挂事务。
- 手动回滚:对于已知的悬挂事务,可以通过手动回滚的方式来释放其占用的资源。
- 优化事务逻辑:简化事务处理逻辑,减少事务执行时间,提高事务的原子性。
- 增强容错机制:增强应用程序的容错机制,确保在异常情况下能够正确处理事务。
- 使用OceanBase提供的工具:OceanBase数据库本身提供了一些工具和功能,如监控工具OCP 中悬挂事务的告警阈值为 10min 、事务管理工具等,可以帮助检测和处理悬挂事务。如OCP 支持对运行中的大事务进行关闭。结束事务的会话后,事务会回滚,会话连接会终止。
排查方法
对于悬挂事务 (suspend_transaction)在Oceanbase v3和v4存在一些视图不同或工具的适用。 更多参考 V3与V4视图变更。
相关视图
__all_virtual_trans_stat: 查询系统中当前所有的活跃事务
State含义 0 THEN 'INIT' 10 THEN 'ACTIVE' 20 THEN 'REDO COMPLETE' 30 THEN 'PREPARE' 40 THEN 'PRECOMMIT' 50 THEN 'COMMIT' 60 THEN 'ABORT' 70 THEN 'CLEAR' 注: V3 state含义 0 init; 1 PREPARE;2 COMMIT; 101 ABORT;4 clear part_trans_action含义 1 THEN 'NULL' 2 THEN 'START' 3 THEN 'COMMIT' 4 THEN 'ABORT' 5 THEN 'DIED' 6 THEN 'END' trans_type 含义 0 本地,2分布式 is_exiting 含义 0 未结束(V$OB_TRANSACTION_PARTICIPANTS的过滤条件),1 结合。
V$OB_TRANSACTION_PARTICIPANTS: (v4) 查询悬挂事务 基于__all_virtual_trans_stat
TX_TYPE 定义 WHEN part_trans_action >= 3 AND trans_type = 0 THEN 'LOCAL' WHEN part_trans_action >= 3 AND trans_type = 2 THEN 'DISTRIBUTED' WHEN trans_type = 0 and state = 10 THEN 'UNDECIDED' WHEN trans_type = 0 THEN 'LOCAL' ELSE 'DISTRIBUTED'
V$OB_GLOBAL_TRANSACTION: (v4) 查询XA 事务 来自SYS.ALL_VIRTUAL_GLOBAL_TRANSACTION
state含义 -1 THEN 'UNKNOWN' 0 THEN 'NON_EXISTING' 1 THEN 'ACTIVE' 2 THEN 'IDLE' 3 THEN 'PREPARED' 4 THEN 'COMMITTED' 5 THEN 'ROLLBACKED' 6 THEN 'PREPARING' 7 THEN 'COMMITTING' 8 THEN 'ROLLBACKING' ELSE 'UNDEFINED
查询长事务
可以通过以下命令查询是否存在悬挂事务与长事务,其中 now() - ctx_create_time > 600
表示创建后执行时间大于 10 分钟的事务。
obclient> SELECT * FROM __all_virtual_trans_stat WHERE (now() - ctx_create_time) > 600;
当前存在的活动事务:
SELECT * FROM __all_trans;
通过查看返回的结果,可以发现那些长时间未完成的事务,并进一步分析其原因,采取相应的措施来处理。
日志收集
在V4以后的版本可以使用obdiag 收集日志,使用 obdiag 时需根据选择在 ~/.obdiag/config.yml
文件中配置集群信息,或在命令中通过 --config
选项配置集群信息。
# 通过 --from/--to 参数指定悬挂事务问题所在的时间区间 obdiag gather scene run --scene=observer.suspend_transaction --from "2022-06-30 16:25:00" --to "2022-06-30 18:30:00" \ --config db_host=xx.xx.xx.xx \ --config db_port=xxxx \ --config tenant_sys.user=root@sys \ --config tenant_sys.password=*** \ --config obcluster.servers.global.ssh_username=test \ --config obcluster.servers.global.ssh_password=****** \ --config obcluster.servers.global.home_path=/home/admin/oceanbase # 已经在 obdiag 配置文件 config.yml 中配置好需要收集节点的登录信息时 obdiag gather scene run --scene=observer.suspend_transaction --from "2022-06-30 16:25:00" --to "2022-06-30 18:30:00"
命令执行后在指定目录下会生成一个文件夹,文件夹形如: obdiag_gather_pack_202xxxxxxx
查看数据库里是否存在长事务:
-- V3 SELECT * FROM OCEANBASE.__all_virtual_trans_stat WHERE part_trans_action <= 2 AND ctx_create_time < date_sub(now(), INTERVAL 600 SECOND) AND is_exiting != 1;
查看事务是否为 XA 事务:
其中事务 ID 为上述 sql 中查出的svr_ip。如果sql执行结果为空,则表明长事务非XA事务;如果执行有结果,则对应的结果即为 XA 事务。
SELECT * FROM OCEANBASE.__all_virtual_global_transaction WHERE trans_id LIKE '%事务ID%'; select * from V$OB_GLOBAL_TRANSACTION where trans_id .... -- 如果存在记录,及为XA事务
查看是否有悬挂事务:
-- V3 SELECT * FROM __all_virtual_trans_stat WHERE part_trans_action > 2 AND ctx_create_time < date_sub(now(), INTERVAL 600 SECOND) AND is_exiting != 1; -- V4 SELECT count(1) FROM GV$OB_TRANSACTION_PARTICIPANTS WHERE CTX_CREATE_TIME < date_sub(now(), INTERVAL 600 SECOND) AND (STATE = 'PREPARE' OR STATE = 'REDO COMPLETE' OR STATE ='PRECOMMIT'); -- 600s 表示默认的事务超时时间
查询具体的表名:
SELECT table_id,table_name FROM __all_virtual_table WHERE data_table_id = 'xxxx'; select table_name,index_status from __all_virtual_table where table_id in ( select /*+READ_CONSISTENCY(WEAK),query_timeout(100000000) */ table_id from __all_virtual_meta_table where data_version != (select value from __all_zone where name = 'global_broadcast_version'));
任何活跃事务都会持有 MemTable 的 reference,如果事务长时间不结束,则会使 MemStore 冻结后无法释放,最终导致内存不足。目前,OCP 的各个版本均支持对悬挂事务和长事务进行监控,一旦报警,需要去确认报警原因,并及时排除风险。有关使用 OCP 监控长事务与悬挂事务的方法,请参见《OCP 告警参考》。
处理方法
如果该事务为 XA 事务,在对应的用户租户下使用 xa rollback 回滚事务;如果该事务为普通事务,则可以通过 OBClient 直连该事务 scheduler 所在节点上的系统租户,通过 kill session 来回滚该事务。后续需要进一步分析悬挂原因(可能的原因包括日志流无主、磁盘写满等)。
可以通过配置 ob_query_timeout
与 ob_trx_timeout
配置项,避免事务长时间不结束,影响系统稳定性。有关以上两个配置项的详细信息与配置方式,请参见 影响 OceanBase 数据库连接超时的相关配置。
- 处理长事务,请执行下列命令。默认使用 sys 租户
- 回滚普通长事务。注意,kill 会话需要直连对应的 OBServer,无需通过 proxy 连接数据库。
SELECT svr_ip FROM OCEANBASE.__all_virtual_processlist WHERE id=会话ID kill 会话ID
- 终止 XA 事务,请执行下列命令。
- 前往系统租户,查找 XA 事务的 XID。
SELECT hex(gtrid),hex(bqual),format_id FROM OCEANBASE.__all_virtual_global_transaction WHERE tenant_id = 租户ID AND format_id <> -2 AND state = 3 AND gmt_modified < date_sub(now(), INTERVAL 1800 SECOND);
- 如果是 Oracle 模式租户,可以前往普通租户查看 XA 事务的 XID。
SELECT rawtohex(gtrid),rawtohex(bqual),format_id FROM sys.all_virtual_tenant_global_transaction_agent WHERE format_id <> -2 AND state = 3 AND ROUND((sysdate - cast(GMT_MODIFIED as date)) * 86400) > 1800;
前往普通租户执行,终止 XA 事务。
set serveroutput on; declare l_xid DBMS_XA_XID; l_ret PLS_INTEGER; BEGIN l_xid.formatid := format_id; l_xid.gtrid := hextoraw('hex(gtrid)'); l_xid.bqual := hextoraw('hex(bqual)'); l_ret := DBMS_XA.XA_ROLLBACK(xid = > l_xid); dbms_output.put_line(l_ret); END; /
- 前往系统租户,查找 XA 事务的 XID。
- 回滚普通长事务。注意,kill 会话需要直连对应的 OBServer,无需通过 proxy 连接数据库。
其它悬挂事务情况
- 事务涉及的分区无主,导致事务状态机推进不了;
- 事务在 leader 上已经推进完成,但由于备机的内存、网络带宽等资源限制,日志同步慢、回放慢,导致事务状态结束慢。
上述两种场景,处理方法是不一样的,下文分别描述。
分区无主
OceanBase 数据库的架构决定分区是有多副本的,多副本中只有一个 leader (主),其它都是 follower (从)。而当 leader 所在 OBsever 宕机了,此时就没有了 leader,少数派副本异常情况下,此时其它副本就会根据 PAXOS 协议重新选主,在此过程中正在运行的事务有可能就产生长事务。
对于这种场景,使用如下方式进行处理:
- 查询当前事务涉及的所有的表信息,SQL 如下,其中
participants
里的每个tid
代表一个表。SELECT svr_ip, trans_id, participants FROM OCEANBASE.__all_virtual_trans_stat WHERE part_trans_action> 2 AND ctx_create_time < date_sub(now(), INTERVAL 600 SECOND) AND is_exiting != 1;
- 分别确认上述分区列表是否有主,SQL 如下,若
role
列皆为 follower,则表示全部是从副本,没有 leader。SELECT svr_ip, svr_port,table_id, partition_idx, role, status, replica_type FROM OCEANBASE.__all_virtual_clog_stat WHERE table_id=表ID;
若为分区无主即当前事务为长事务,可按照长事务进行处理。
副本应用日志慢
副本应用日志慢的原因有很多。比如某个 OBSever 服务器突然资源异常,OS 上跑任务消耗了大量的 IO、网络等资源,导致该服务器上的 OBSever 响应慢;主副本分布不均匀,某服务器上的 leader 特别多,leader 进行应用需求响应,消耗大量资源,导致 follower 的可用资源比较少,从而一直追不上该 follower 对应的 leader;某个 OBserver 服务器硬件异常,比如网卡异常,导致 PAXOS 协议传到 follower 非常慢,一直处于等待中,追赶中;该 OBSever 正在合并或备份中等。
对于这种场景,使用如下方式进行处理:
- 查询当前事务涉及的所有的表信息,SQL 如下,其中
partition
里的每个tid
代表一个表。SELECT svr_ip, svr_port,trans_id, 'partition' FROM OCEANBASE.__all_virtual_trans_stat WHERE part_trans_action > 2 AND ctx_create_time < date_sub(now(), INTERVAL 600 SECOND) AND is_exiting != 1;
- 根据第一步查询到的
partition
中记录的table_id
信息,确认当前pkey
对应的leader
位置。SELECT svr_ip,svr_port FROM OCEANBASE.__all_virtual_clog_stat WHERE role='LEADER' AND table_id = 表ID;
如果第一步和第二步对应的
svr_ip
不同,说明异常事务在 follower 上存在。 - 查询上述事务对应的分区,目前集群中的记录条数。
SELECT count(1) FROM OCEANBASE.__all_virtual_trans_stat WHERE trans_id LIKE '%{选取第一步 trans_id 中的 hash 字段}%'AND`partition` LIKE '%xxx%';
如果上述查询结果为 1 条,说明只有备机上存在。这种情况请联系OceanBase 技术支持团队处理。
总之,悬挂事务是数据库管理中需要关注的一个方面,通过对事务的合理管理和优化,可以有效地避免或减少悬挂事务的发生,从而保证数据库系统的稳定性和性能。
对不起,这篇文章暂时关闭评论。