问题现象
OceanBase 数据库在 V3.x 版本前并不支持大事务,用户可以通过配置项 _max_trx_size 来控制事务的大小,从 V3.x 版本开始,OceanBase 数据库具有了支持大事务的能力,同时也在 OceanBase 数据库 V3.1.x、V3.2.x(V3.2.4 除外)版本中去掉了事务大小的配置项 _max_trx_size。 然而 OceanBase 数据库 V3.x 版本支持大事务的能力具有一定局限性,当超多参与者的事务 * 并发负载 达到 OceanBase 数据库的能力上限时,会导致处在提交阶段的事务本身推进不下去,一直卡在事务的协调者等待事务参与者的消息回复上,最终导致租户队列堆积、request 推入队列 -4019 报错、节点 CPU 飙高、节点网络打爆等异常情况,且一般情况下系统无法自己恢复,系统一旦遇到该问题需要运维介入应急解决。OceanBase 数据库 V3.2.3 BP6 版本对事务的参与者个数处理能力做了一部分提升优化,但整体上还是存在一定的局限性。因此在 OceanBase 数据库 V3.x 版本使用大事务时,需要明确大事务参与者数量的局限性,尽量控制事务参与者数量来规避该问题。
OceanBase 数据库建议的事务参与者数量:
- 单节点上并发的普通事务参与者数量小于 30000 个。
- 单节点上并发的XA事务参与者数量小于 10000 个(XA 事务因事务消息内容更大,因此参与者数量应更小)。
事务参与者过多可能引发的异常情况举例。
OceanBase 数据库出现悬挂事务报警,日志含大量 -4019 报错。
WARN [STORAGE.TRANS] process (ob_trans_rpc.h.219) [182156][0][xxx-xxx-xxx-xxx] [lt=11] [dc=0] transaction rpc error(rcode={code:-4019,msg:"",warnings:[]},pkey={tid:1000,partition_id:1000,part_cnt:1000}, trans_id={hash:xxxxx,inc:80845,addr:"xx.xx.xx.xx:xx",t:xxxxx}, dst="xx.xx.xx.xx:xx", msg_type=7)主机上存在租户队列积压(req_queue:total_size 表示租户积压的请求数量)。
INFO [SERVER.OMT] ob_multi_tenant.cpp:819 [23311][0][xxx-xxx-xxx-xxx] [lt=17] [dc=0] dump tenant info(tenant={id:1002, compat_mode:0, unit_min_cpu:"8.000000000000000000e+00", unit_max_cpu:"1.200000000000000000e+01", slice:"0.000000000000000000e+00", slice_remain:"0.000000000000000000e+00", token_cnt:42, sug_token_cnt:42, ass_token_cnt:42, lq_tokens:4, used_lq_tokens:0, stopped:false, idle_us:4395011, recv_hp_rpc_cnt:935154, recv_np_rpc_cnt:10925, recv_lp_rpc_cnt:0, recv_mysql_ps_close_cnt:0, recv_mysql_cnt:7780, recv_task_cnt:219, recv_large_req_cnt:1911, tt_large_quries:45177, pop_normal_cnt:71139024, actives:42, workers:42, nesting workers:7, lq waiting workers:0, req_queue:total_size=15264 queue[0]=15262 queue[1]=0 queue[2]=0 queue[3]=0 queue[4]=2 queue[5]=0 , large queued:0, reserve queued:0, multi_level_queue:total_size=0 queue[0]=0 queue[1]=0 queue[2]=0 queue[3]=0 queue[4]=0 queue[5]=0 queue[6]=0 queue[7]=0 , recv_level_rpc_cnt:cnt[0]=0 cnt[1]=0 cnt[2]=1 cnt[3]=0 cnt[4]=0 cnt[5]=390 cnt[6]=0 cnt[7]=0 , group_map:null, rpc_stat_info: pcode=0x750:cnt=204851 pcode=0x701:cnt=3894 pcode=0x521:cnt=3498 pcode=0x51f:cnt=316})
问题原因
- OceanBase 数据库 V3.x 之前的版本不支持大事务,这意味着如果在 OceanBase 数据库 V1.x 或者 V2.x 版本使用大事务,最终会导致内存报错、超时或者是其他错误,事务本身由于上述错误从而会终止并回滚,因此大事务不会对系统级资源存在长久的霸占持有,进而对系统整体造成风险。(当然使用 OceanBase 数据库 V1.x、OceanBase 数据库 V2.x 前,通常也需要做相应地适配调整。)
- 从 V3.x 版本开始,OBServer 具有了支持大事务的能力,同时也在 OceanBase 数据库 V3.1.x、V3.2.x(V3.2.4 除外)版本中去掉了事务大小的配置项
_max_trx_size。然而 OceanBase 数据库 V3.x 版本支持大事务的能力具有一定局限性,当事务参与者数量达到 OceanBase 数据库的能力上限时,会导致处在提交阶段的事务本身推进不下去,一直卡在事务的协调者等待事务参与者的消息回复上,最终导致租户队列堆积 hang 死等一系列严重情况。
问题的风险及影响
会导致租户中的事务呈现无法向前推进,最终消耗资源,租户请求没有相应,具体有可能造成队列堆积、observer.log 中 -4019 报错、CPU 高或者是网络资源打爆等问题。且一般情况下系统无法自己恢复,需要进行运维介入才有可能恢复系统。
影响的版本
OceanBase 数据库企业版 V3.x 版本。
解决方法及规避方式
预防监控(核心)
控制单表分区数目,减降本身不必要的分区。
比如一种常见的问题场景是:事务中包含了查询、更新、插入某个或者某几个分区数量非常多的表,有可能因为索引选择或者是表分区设置等原因导致 SQL 裁剪后请求还是落入到了非常多分区或者是全表扫,最终导致事务参与者数量非常多。因此在初始使用 OBServer 时需要评估单表的分区数目,针对过百甚至是过千的分区表,尽量进行降分区和控制分区数的操作。
增加对事务参与者个数的监控,当出现参与者过多的SQL时及时进行调优。
对于本身系统中存在了比较多分区表的环境,且无法有效地降低分区数时,需要在 OCP 中实时监控系统中是否存在参与者过多的事务。
OCP 在 topsql 页面 monitor 访问分区过多的 SQL(15天的数据量):进入租户管理后 topsql > 列管理 > 勾选访问分区数。
从 OCP V3.3.4 版本对多个参与者的 SQL(大于 2 个)会作为可疑 SQL 暴露出来,直接出现在:租户管理 > SQL诊断 > 可疑 SQL 中。
如果想要直接监控事务参与者个数,可以黑屏监控下面的 SQL。
SELECT count(*) FROM oceanbase.__all_virtual_trans_stat where part_trans_action > 2 GROUP BY trans_id;增加参与者过多时的日志 WARN 监控优化(适用于 V3.2.3 BP5 与 V3.1.2 BP10 Hotfix 及以上版本的 OBServer):在 OCP上告警 > OB 日志告警 > 增加过滤关键字增加 "too many partitipants for 2pc" 关键日志的告警。
V3.2.4 版本又找回了配置项
_max_trx_size,因此在业务允许(不会对其他事务造成影响)可以配置该参数控制事务大小。
问题发生后的确认与处理
问题确认
当 OceanBase 数据库发生与 “问题现象” 中类似的异常情况,且怀疑与本问题有关时,可在 sys 租户下使用以下 SQL 辅助判断。
SELECT count(*), trans_id FROM oceanbase.__all_virtual_trans_stat where part_trans_action > 2 GROUP BY trans_id order by count(*) desc limit 30;
返回结果如下(可见:事务参与者数量达 13398 * 5 = 66990 个)。
+----------+---------------------------------------------------+
| count(*) | trans_id |
+----------+---------------------------------------------------+
| 13398 | {hash:xxx,inc:xxx,addr:"xx-xx-xx-xx:2882",t:xxx} |
| 13398 | {hash:xxx,inc:xxx,addr:"xx-xx-xx-xx:2882",t:xxx} |
| 13398 | {hash:xxx,inc:xxx,addr:"xx-xx-xx-xx:2882",t:xxx} |
| 13398 | {hash:xxx,inc:xxx,addr:"xx-xx-xx-xx:2882",t:xxx} |
| 13398 | {hash:xxx,inc:xxx,addr:"xx-xx-xx-xx:2882",t:xxx} |
+----------+---------------------------------------------------+
问题处理
当发现事务无法推进的情况后,可以先检查分区 leader 情况,也就是检查是否存在分区无主。如果确定事务所涉及的分区均存在 leader 的情况下,可以尝试采用以下措施来恢复系统。
在系统租户下,调大参数
trx_2pc_retry_interval,命令如下。obclient> ALTER SYSTEM SET trx_2pc_retry_interval = '5s';执行完成后,可以通过查看虚拟表
__all_virtual_trans_stat来观察事务提交的推进情况。如推进依旧缓慢,可对事务协调者所在的分区切主。
obclient> ALTER SYSTEM SET primary_zone=xxx;如依旧无效果,可尝试重启集群。
obclient> ALTER SYSTEM start '节点 IP:PORT';