GC 流程涉及到 RS 的状态切换和 LS 的资源安全回收,流程上较长。且 GC 线程每个租户仅有一个,某个日志流 GC Hang 死时会卡住所有其余日志流的 GC,进而造成更大的影响。
本文档会帮助大家快速定位到 GC 故障的模块,直达问题核心。
基本概念
在 OceanBase 数据库 V4.x 版本中,GC 的条件只有两个,一是不在成员列表中,二是 __all_ls_status
表中对应 LS 被标记为进入 GC 状态。对于不在成员列表中的副本,我们直接调用 ls service 的remove ls 接口即可,不需要对表做任何处理。对于标记删除的副本,leader 会和 RS 通过推进状态机到删除 __all_ls_status
表中对应 ls 的行,然后所有副本都会正常的 remove ls。 总之,在当前 leader 的成员列表中且 __all_ls_status
中对应行依然存在的日志流,是不满足 GC 条件的,因此不会开始 GC,判断 GC 问题需要先明确此条件。 remove ls 流程基本上流程可重入。当某个日志流的某个模块 remove 不满足条件时,可以退出此轮,避免 GC 线程卡住。
常见问题排查思路
找到 GC 卡住的 LS
-
如果明确怀疑某 LS 没有 GC 时(类似迁移失败场景),首先需要确定该 LS 是否满足 GC 条件,具体可查询
__all_virtual_log_stat
表。obclient> select * from __all_virtual_log_stat where tenant_id = xxx and ls_id = xxx;
如果某副本不在 Leader 的成员列表中,则满足 GC 条件。否则继续查
__all_virtual_ls_status
表:obclient> select * from __all_virtual_ls_status where tenant_id = xxx and ls_id = xxx;
如果结果存在并且
status
为NORMAL
,则不满足 GC 条件。结果不存在或不为 NORMAL 都满足条件。只有满足 GC 条件的 LS 才有继续分析的必要,否则请分析到底是成员列表更新失败或是 ls_status 更改状态失败。
-
如果没有怀疑的 LS 目标,只知道租户 GC 卡住了,则需要找到卡住的 LS。
obclient> select ls_id, gc_state, gc_start_ts from __all_virtual_ha_diagnose where tenant_id = xxx and svr_ip = 'xxx' and svr_port = xxx;
- 查询结果中
gc_start_ts
不为 -1 的就是卡住的日志流,如果有复数个不为 -1 的日志流,选择任意一个即可。 - 如果所有 LS 的结果均为上图所示,gc_state 为 NORMAL 且 gc_start_ts 为 -1,表示所有日志流都不满足 GC 条件。需要分析删除租户的命令执行是否有问题。
- 查询超时,大概率有 LS 的死锁,直接抓堆栈查看 Txxxx_GC 线程是否有死锁。
- 查询无结果,但是 Unit 依然没有删除,说明 GC 卡在了某个日志流的析构或资源释放,可通过
grep Txxxx_GC observer.log | grep id:xxxx
查询。
- 查询结果中
找到卡住的模块
在确定卡住的日志流之后,下一步需要确定卡住的具体模块,具体可查询 __all_virtual_ha_diagnose
表。
obclient> select ls_id, gc_state, gc_start_ts from __all_virtual_ha_diagnose where tenant_id = xxx and svr_ip = 'xxx' and svr_port = xxx;
-
如果查询结果不存在,则说明 GC 流程中状态机的推进已经完成,LS 进入了 remove ls 阶段并且已经从 ls 的 map 中摘掉了,剩下的就是等待这个日志流满足 safe_to_destroy 的条件后安全析构。在日志中搜索该日志流的 GC 线程日志,应该可以搜到循环报错的模块,此模块就是不满足 safe_to_destroy 的根因。
grep Txxxx_GC observer.log | grep id:xxxx
-
如果查询结果存在,则代表 GC 流程卡在某个状态机的推进过程中,如卡在等待事务全部结束,同样搜索 GC 线程的日志关于此日志流循环打印的地方找到根因。
-
如果存在结果不存在,则大概率 GC 线程死锁,抓堆栈查看 GC 线程即可。
适用版本
OceanBase 数据库 V4.x 版本。