首批通过分布式安全可靠测评,为关键业务系统打造
合并问题排查
更新时间:2026-06-19 09:35:18
本文主要介绍合并慢、超时问题的排查方法。
适用版本
OceanBase 数据库所有版本
问题排查思路
在进行合并问题排查时,首先要确定当前集群中的合并配置项,确定当前的合并状态,然后根据查询得到的情况,对不同的合并问题类型进行分析后再寻找根因。
确认合并配置项
OceanBase 数据库中包含以下合并相关的配置项:
enable_manual_merge:是否开启手动合并,取值为TRUE表示需要手动合并,默认为FALSE。zone_merge_concurrency:用于设置在合并时,支持多少个 Zone 并发。当值为0时,由系统根据部署情况自动选择最佳并发度,一般不需要设置。默认为1,表示单次只有一个 Zone 进行合并。zone_merge_order:用于设置 Zone 的轮转合并顺序。不指定时,由系统自动决定,一般不需要设置。enable_merge_by_turn:用于设置是否开启轮转合并策略,设置为TRUE时表示开启轮转合并,默认为FALSE。major_freeze_duty_time:每日合并发起时间。enable_auto_leader_switch:用于设置是否开启自动切主,默认为TRUE。
有关以上系统配置项的详细信息,请参见《OceanBase 数据库 参考指南》中的 配置项参考 章节。
确认合并状态
本例中,当前的 frozen_version 为 25,表示集群需要合并到 25 版本,但 cn-shanghai-e 这个 Zone 副本只合并到了 24 版本。
obclient> SELECT * FROM __all_zone WHERE name = "frozen_version" or name = "last_merged_version";
+----------------------------+----------------------------+---------------+---------------------+-------+------+
| gmt_create | gmt_modified | zone | name | value | info |
+----------------------------+----------------------------+---------------+---------------------+-------+------+
| 2020-12-07 16:01:30.793286 | 2020-12-30 02:00:00.445594 | | frozen_version | 25 | |
| 2020-12-07 16:01:30.793490 | 2020-12-29 02:01:12.449651 | | last_merged_version | 24 | |
| 2020-12-07 16:01:30.794375 | 2020-12-29 02:01:11.880114 | cn-shanghai-e | last_merged_version | 24 | |
| 2020-12-07 16:01:30.795109 | 2020-12-30 02:01:15.563291 | cn-shanghai-f | last_merged_version | 25 | |
| 2020-12-07 16:01:30.795842 | 2020-12-30 02:01:22.694016 | cn-shanghai-g | last_merged_version | 25 | |
+----------------------------+----------------------------+---------------+---------------------+-------+------+
5 rows in set (0.00 sec)
继续查询 __all_zone 表,查看 cn-shanghai-e 的合并状态,发现状态为 MERGING,表示正在合并。
obclient> SELECT * FROM __all_zone WHERE name = "merge_status";
+----------------------------+----------------------------+---------------+---------------------+---------+------+
| gmt_create | gmt_modified | zone | name | value | info |
+----------------------------+----------------------------+---------------+---------------------+---------+------+
| 2020-12-07 16:01:30.793286 | 2020-12-30 02:00:00.445594 | | merge_status | MERGING | |
| 2020-12-07 16:01:30.794375 | 2020-12-29 02:01:11.880114 | cn-shanghai-e | merge_status | MERGING | |
| 2020-12-07 16:01:30.795109 | 2020-12-30 02:01:15.563291 | cn-shanghai-f | merge_status | IDLE | |
| 2020-12-07 16:01:30.795842 | 2020-12-30 02:01:22.694016 | cn-shanghai-g | merge_status | IDLE | |
+----------------------------+----------------------------+---------------+---------------------+---------+------+
5 rows in set (0.00 sec)
合并问题类型
根据上面查询到的信息,一般情况下可以分为以下几类:未合并问题、合并超时问题、合并慢问题。下面针对每一类问题进行详细的分析。
未合并问题
未合并问题分为两类:RootService 没有发起相关 Zone 的合并和副本未合并。
Zone 未合并
根据上面的信息,发现正在合并,此时查询该 Zone 的 global_broadcast_version 与 broadcast_version。 如果 broadcast_version 等于 last_merged_version,且 last_merged_version 落后于 global_broadcast_version,说明 RootService 没有发起相关 Zone 的合并 。
obclient> SELECT * FROM __all_zone WHERE name = "global_broadcast_version" or name = "broadcast_version";
+----------------------------+----------------------------+---------------+--------------------------+---------+------+
| gmt_create | gmt_modified | zone | name | value | info |
+----------------------------+----------------------------+---------------+--------------------------+---------+------+
| 2020-12-07 16:01:30.793286 | 2020-12-30 02:00:00.445594 | | global_broadcast_version | 25 | |
| 2020-12-07 16:01:30.794375 | 2020-12-29 02:01:11.880114 | cn-shanghai-e | broadcast_version | 24 | |
| 2020-12-07 16:01:30.795109 | 2020-12-30 02:01:15.563291 | cn-shanghai-f | broadcast_version | 25 | |
| 2020-12-07 16:01:30.795842 | 2020-12-30 02:01:22.694016 | cn-shanghai-g | broadcast_version | 25 | |
+----------------------------+----------------------------+---------------+--------------------------+---------+------+
5 rows in set (0.00 sec)
对于 RootService 未发起相关 Zone 合并的情况,按以下方法排查:
排查 RootService 合并调度线程是否有报错。
如果有返回值,则说明合并调度线程存在错误。
grep "daily.*merge.*ret=-" rootservice.log检查集群是否正在补副本。 如果以下查询有返回值,则说明正在进行副本的负载均衡。
obclient> SELECT count(*) FROM __all_virtual_replica_task;
副本未合并
本例继续假设当前要合并的目标版本是 25,查询 meta 表查看 data_version != 25 的副本以缩小排查范围。
对于 OceanBase 数据库 V1.X 版本,查询
__all_virtual_core_meta_table、__all_virtual_core_root_table、__all_root_table与__all_meta_table表。对于 OceanBase 数据库 V2.X 及后续版本,查询
__all_virtual_core_meta_table、__all_virtual_core_root_table、__all_root_table与__all_virtual_meta_table表。
obclient> SELECT * FROM __all_virtual_meta_table WHERE data_version != 25 LIMIT 10;
+-----------+------------------+--------------+---------------+----------+----------------------------+----------------------------+----------+---------+---------------+---------------+------+-----------------------------------------------------------------------------------------------------------+-----------+-----------+--------------+---------------+--------------+-----------------+--------------------+--------------------+-------------+---------+--------------+---------------+-----------------------+------------+--------------------+--------+-----------+--------------------+------------------+
| tenant_id | table_id | partition_id | svr_ip | svr_port | gmt_create | gmt_modified | sql_port | unit_id | partition_cnt | zone | role | member_list | row_count | data_size | data_version | data_checksum | row_checksum | column_checksum | is_original_leader | is_previous_leader | create_time | rebuild | replica_type | required_size | status | is_restore | partition_checksum | quorum | fail_list | recovery_timestamp | memstore_percent |
+-----------+------------------+--------------+---------------+----------+----------------------------+----------------------------+----------+---------+---------------+---------------+------+-----------------------------------------------------------------------------------------------------------+-----------+-----------+--------------+---------------+--------------+-----------------+--------------------+--------------------+-------------+---------+--------------+---------------+-----------------------+------------+--------------------+--------+-----------+--------------------+------------------+
| 1001 | 1100611139463766 | 0 | xxx.xxx.x.xa | xxxx | 2020-12-29 10:34:15.176561 | 2020-12-29 10:34:15.205753 | 2881 | 1001 | 0 | cn-shanghai-e | 1 | xxx.xxx.x.xx:2882:1609209255175464,xxx.xxx.x.xx:2882:1609209255175464,xxx.xxx.x.xx:2882:1609209255175464 | 0 | 0 | 24 | 0 | 0 | | 0 | 1609209255204831 | 0 | 0 | 0 | 0 | REPLICA_STATUS_NORMAL | 0 | 0 | 3 | | 0 | 100 |
+-----------+------------------+--------------+---------------+----------+----------------------------+----------------------------+----------+---------+---------------+---------------+------+-----------------------------------------------------------------------------------------------------------+-----------+-----------+--------------+---------------+--------------+-----------------+--------------------+--------------------+-------------+---------+--------------+---------------+-----------------------+------------+--------------------+--------+-----------+--------------------+------------------+
1 row in set (0.07 sec)
根据上面的信息,发现是 pkey 为{tid:1100611139463766, partition_id:0} 的表在 xxx.xxx.x.xa:xxxx 机器上未合并。
也可以通过搜索 rootservice.log 定位是哪个表阻塞了合并。
合并超时问题
定位未合并到指定版本的 partition。
进数据库查询
例如当前要合并的目标版本是 25,可以去 meta 表看
data_version != 25的副本,从而缩小排查范围。说明
这里 meta 表指一组表,一般先看最后一级 meta 表
__all_meta_table/__all_virtual_meta_table)有没有 data_version 没推上去的副本,没有的话,查看更上一级的 meta 表。 对于 V2.0 以下版本,meta 表指:__all_virtual_core_meta_table、__all_virtual_core_root_table、__all_root_table、__all_meta_table。 对于 V2.0 及以上版本,meta 表指__all_virtual_core_meta_table、__all_virtual_core_root_table、__all_root_table、__all_virtual_meta_table。在数据库后台查询。
以
not merged为关键词搜索 RS 所在机器上的最新的rootservice.log。
判断该 partition 的合并任务是否在执行中。
SELECT * FROM __all_virtual_sys_task_status;可以通过该虚拟表拿到执行的 trace,然后去对应机器上查询,判断合并任务是否卡住。
如果该 partition 没有调度合并任务,判断该 partition 的最新的转储 SSTable 的
frozen_version有没有推过freeze_info点,如果没有推过freeze_info点,说明转储有问题。SELECT * FROM __all_virtual_table_mgr WHERE table_id = xxx and partition_id = xxx; SELECT * FROM __all_virtual_freeze_info;如果没有获取任何有用信息,可以搜索最近一段时间的
observer.log的 ERROR 日志来获取线索。
合并慢问题
如果合并还未结束,可以按照合并超时的排查路径进行排查。如果合并已经完成,可以按照以下步骤进行排查。
通过 SQL 查询指定 Major 版本的合并信息统计,找到合并耗时最多的几个 partition 来具体分析。
SELECT /*+ query_timeout(10000000)*/* FROM __all_virtual_partition_compaction_history WHERE merge_type = "major merge" and merge_version = "<merge_version>" ORDER BY (merge_finish_time - merge_start_time) desc limit 5;说明
version有三个字段,第一个非 0 字段是merge_version,查询时需要替换成本次合并的merge_version,后两个字段保持为0即可。您可通过
__all_zone表可以查到本次合并的merge_version。通过表
__all_virtual_partition_sstable_macro_info查看宏块重用情况。步骤 1 中的
occupy_size、macro_block_count、use_old_macro_block_count、rewrite_macro_old_micro_block_count、rewrite_macro_total_micro_block_count等字段,分别表示该 partition 的数据量、宏块数、重用宏块数、重用宏块中重写的微块数。如果重用宏块数量 、总宏块数的比率非常低有可能会产生合并变慢的情况,这种情况有可能发生在新导入表的第一次合并或随机写严重,导致发生了全量合并。查看合并开始时间是否合理。
SELECT * FROM __all_virtual_partition_sstable_macro_info WHERE table_id = xxx and partition_id = xxx and svr_ip = "xxx" ORDER BY merge_finish_time desc LIMIT 10;找到合并开始最晚的几个 partition,在该时间段的
observer.log中搜索其 compaction 记录。grep "sstable merge finish.* table_id.* partition_id" observer.log.20211117* | vi -如果有报错信息,
a. V3.x 之前的版本,报错
-4288(constexpr int OB_MEMTABLE_CANNOT_MINOR_MERGE = -4288;),表示 MemTable 上有未决事务导致转储失败,需要进一步对事务层面进行排查。b. 其他报错需要在合并模块进行排查。
从合并实现的原理来说,合并会将有修改的数据宏块打开重写(速度慢),没有修改的数据宏块直接重用(将老的数据宏块直接刷盘,速度快)全量合并,即所有的宏块都打开重写,所以有可能会造成速度慢的问题。
在 V3.2 及以后的版本上,提高了支持功能,您可以查询表__all_virtual_compaction_suggestion 来进一步诊断合并慢的原因。
问题排查步骤
登录到未完成合并的 OBServer,查看是否存在磁盘 I/O 瓶颈。
在 Shell 中执行
iostat命令查看服务器的合并状态,如果使用率 100%,并且await值偏高,那么很有可能硬件出现了异常,需要检查硬件状况。[root@hostname /]# iostat -x 1 -k如果通过
iostat命令检查的await值偏低,但整体的 I/O 带宽不高,可以在 Shell 中执行以下命令查看是否存在限速。[admin@hostname log]$ grep "iostat" observer.log通过以上命令得到 OBServer 的 I/O 统计信息中,有
sys_io_percent与sys_iops_up_limit两个字段。其中sys_io_percent表示当前系统 I/O 的使用百分比,sys_iops_up_limit表示系统 2MB I/O 能力的上限。如果
sys_iops_up_limit大于iostat的带宽使用量,则说明 I/O 余量充足,但 CPU 存在瓶颈,无法使用更多的带宽。如果
sys_iops_up_limit小于iostat的带宽使用量,则说明 I/O 可能被限速了,需要调整最大允许使用的 I/O。OceanBase 数据库的 I/O 速度限制由sys_bkgd_io_low_percentage与sys_bkgd_io_high_percentage控制,分别表示sys_io_percent的下限与上限。如果下限过低,则可能导致合并过慢,可以适当调整这个配置项调大 I/O 限制。有关以上配置项的详细信息,请参见《OceanBase 数据库 参考指南》中的 系统配置项 章节。
如果上述步骤中,发现性能瓶颈为 CPU,可以调整
merge_thread_count配置项调整合并的线程数。merge_thread_count用于设置每日合并工作的线程数,默认为0,表示线程数由系统决定,取值范围为[0,64]。有关该配置项的详细信息,请参见《OceanBase 数据库 参考指南》中的 系统配置项 章节。
经过以上排查步骤后,仍不能解决问题,则还有可能是以下场景中写入的数据量太大导致的合并时间过长:
由于积攒了过多的转储,新增的数据过多,此时可以适当调小
minor_freeze_times来更频繁的触发合并。minor_freeze_times用于设置单个租户冻结多少次后触发一次全局合并,默认为5,取值范围为[0,65535]。设置为0时,表示只要冻结就会自动触发一次合并。有关该配置项的详细信息,请参见《OceanBase 数据库 参考指南》中的 系统配置项 章节。
写入放大过大,由于合并的时候需要将基线和转储的数据合起来,如果基线的宏块和增量数据有交集,就需要打开重写;如果增量数据的写入非常离散那么就会导致写入放大很多。这种情况可以适当调大
merge_thread_count来缓解。注意
业务上应尽量避免放大过大,主键尽量不要使用写入会非常随机的字段。例如,在使用 OceanBase 数据库时需要避免使用 UUID 做主键。/p>