首批通过分布式安全可靠测评,为关键业务系统打造
排查 OceanBase 数据库 V4.0 版本中 ReplayService 问题
更新时间:2024-05-09 11:22
Replay service 作为 Follower 回放日志主路径的调度者,在 OBServer 出现非预期问题时会作为屏障阻止后续日志的回放,以防止出现更严重的正确性问题。但是非预期的错误往往并不在 Replay service 自身,而是出在各个模块不同类型日志的具体回放逻辑中,本文旨在方便快速定位到出问题的源头,加快处理问题的效率。
常见问题 1 :回放卡住/回放位点不推进 /pre_check 的 ERROR 报错
如何确认回放卡
首先需要明确回放卡的语意,回放位点小于同步位点,如果回放位点等于同步位点,则是同步本身出了问题。
可以在 SYS 系统租户下,通过以下查询确认日志流副本的同步位点(end_scn),已提交回放的位点(unsubmitted_log_scn)和在回放队列中等待回放的日志任务数(pending_task_count)。
select a.svr_ip, a.svr_port, a.tenant_id, a.ls_id, b.end_scn, a.unsubmitted_log_scn, a.pending_cnt from oceanbase.__all_virtual_replay_stat a join oceanbase.__all_virtual_log_stat b on a.svr_ip=b.svr_ip and a.svr_port=b.svr_port and a.tenant_id=b.tenant_id and a.ls_id = b.ls_id and a.role='FOLLOWER';
如果 unsubmitted_log_scn 和 pending_task_count 长时间不变化,同时 unsubmitted_scn 小于 end_scn 则回放卡住。
排查步骤
排除回放线程卡
obstack $pid -ao 连续执行两次,查找是否有 Txxx_ReplayS 线程的堆栈没有变化。 同时用疑似卡的线程号过滤日志(grep $thread_id observer.log),如果线程长时间没有打印日志,则是卡住了,请联系 OceanBase 技术支持人员协助排查,有关线程卡的原因。
回放报 fatal error,即不可重试的错误
排查步骤:
首先确定出问题的租户(后文用 T1xxx 表示)和日志流(后文用 id:xxx 表示),查看对应日志流的回放是否遇到
fatal error即不可重试的报错,这种类型的回放报错会打印 error 日志:grep 'T1xxx_ReplaySrv' observer.log | grep 'id:xxx' | grep 'ReplayService has encountered a fatal error' | grep ERROR输出内容如下:

此日志会重复打印,根据其中任意一条的
err_ts_找到对应的报错时间(select usec_to_time(err_ts_) from dual),err_ret_是回放报错的错误码。搜索报错时间附近的 OBServer 日志找到报错的起始点,并找到对应的线程。
grep 'T1xxx_Replay' observer.log.xxxx | grep 'id:xxx' | grep process_replay_ret_code_输出内容如下:

根据线程找到回放报错时的详细链路堆栈信息,分析链路中错误码吐出的原因即可定位问题。
grep '$thread_id]' observer.log.xxxx | vim -输出内容如下:

其他场景
可以先查看当前日志流最小未连续回放位点相关信息。
grep 'get_min_unreplayed_log_info' observer.log | grep 'T1xxx' | grep 'id:xxx'输出内容如下:

首先查看
is_enabled字段是否为 true,如果为 false,则回放未启动,符合预期。 然后观察结果中的 scn,观察 scn 是否变化:如果几次的结果均不同,则可以转到 常见问题 2:回放慢 这一节,按照回放慢的逻辑排查。
如果几次结果均相同,则表示回放位点卡住。转到步骤 2 。
需要继续日志确认卡的原因。
grep "submit replay task need retry" observer.log | grep "Txxx_" | grep "id:xxxx"输出内容如下:

其中
is_tenant_out_of_mem=true表明 MEMStore 没有内存导致,请联系 OceanBase 技术支持人员协助排查,确认 MEMStore 内存爆的原因。is_wait_for_barrier表明当前在等barrier日志回放。
通过
grep 'T1xxx_ReplaySrv' observer.log | grep 'id:xxx' | grep WDIAG可以看到回放报错的日志。再根据线程号找到报错的上下文。 此外也可以用下图中的 scn 来过滤对应日志流的回放日志:
另外通过
grep 'T1xxx_ReplaySrv' observer.log | grep 'id:xxx' | grep $scn命令,通常可以发现一些重复打印的日志,分析这些日志可以找到一直重试的原因,如果 Clog 日志太多,上面的搜索中可以屏蔽掉 Clog 的日志,集中找其他模块的重复日志。
常见问题 2:回放慢
怀疑回放慢之前请先排查是否有卡住的问题。
查
__all_virtual_replay_stat表,观察end_lsn和unsubmitted_lsn的差距是否持续增大,此差值代表follower回放落后leader确认的日志量。
如果
pending_cnt持续不为 0 并保持在一个较高的数值(百级别以上),很可能单条回放慢的日志较多导致回放整体延迟,搜索单条回放慢的 OBServer 日志。通过
grep 'cost too much time' observer.log | grep T1xxx | grep 'id:xxx'命令过滤关键词,获取回放信息。如果此类单条回放耗时 10ms 级及以上的日志较多,可以根据日志类型和线程号查看此日志回放时具体模块相关的日志。

如果
pending_cnt基本保持一个很低的值但end_lsn和unsubmitted_lsn的差距依然递增,很可能是因为barrier较多导致,可以搜索barrier回放日志。通过
grep 'barrier log replay succ' observer.log | grep T1xxx | grep 'id:xxx'命令过滤关键词,获取barrier回放日志信息。统计一段时间内此日志的打印数可以大致估计出
barrier日志的数量,如果barrier数量较多则会影响回放。
如果
barrier日志不是很多,继续排查是否为热点行。通过
grep 'too much trans node scaned when row compact' observer.log | grep T1xxx_Replay命令过滤关键词,获取关键日志信息。如果发现此类日志,请联系 OceanBase 技术支持人员协助排查,继续分析是否为根因。
搜索如下日志排除回放限速导致回放进度推进慢。
通过
grep Txx_ReplayS observer.log |grep "Throttling"命令过滤关键词,如果发现此类日志可以确定是限速导致回放进度推进慢。如果上述关键日志均不存在,可以根据表中的
unsubmitted_lsn的增速算出拉日志的速度(隔 10s 中观察此列的增长值),目前回放的拉日志速度瓶颈为 400MB/s,如果速度基本达到此数量级说明已经达到了目前架构下follower回放的峰值,可以考虑降低leader的并发。
常见问题 3:漏回放日志
出于性能考虑,回放并不会每条日志回放时都打日志,但依然可以根据一些信息协助分析问题
在日志流创建时间点附近搜索。
通过
grep 'enable replay status' observer.log.xxx | grep T1xxx | grep 'id:xxx'命令过滤关键词,获取相应信息。主要关注
base_scn,此时间戳为日志回放的起点,所有小于此ts的日志均不会回放。如果问题日志小于base_scn但数据不正确,可以继续排查是否转储位点推进不正常,或找到enable replay的线程分析入参的base_scn是否不合理。
搜索最大已回放位点是否已经推过问题日志。
通过
grep 'get_min_unreplayed_log_info' observer.log | grep 'T1xxx' | grep 'id:xxx'命令过滤关键词,获取相应信息。
scn代表当前回放的上界,此scn之前的日志都已回放,如果问题日志的scn大于已回放位点,那么并没有漏,只是还没有回放。如果问题日志的
scn落在base_scn和最大回放位点之间,请联系 OceanBase 技术支持人员协助排查。
适用版本
OceanBase 数据库 V4.x 版本。