首批通过分布式安全可靠测评,为关键业务系统打造
增量 schema 刷新报错 OB_EAGAIN(-4023)的原因和解决方法
更新时间:2026-07-02 15:41
问题现象
在 OceanBase 数据库 V4.x 版本环境下进行增量 Schema 刷新时,出现 OB_EAGAIN(错误码 -4023)错误而导致刷新失败。
关键诊断信息
在增量的 Schema 刷新时出现下面的日志和报错信息。
[2025-04-16 02:32:38.933683] ERROR [RS] refresh_schema (ob_ddl_service.cpp:28745) [3862302][DDLTransCtr][T0][xxxxx-xxxxx-xxxxx-xxxxx] [lt=3][errcode=-5410]
Refresh schema failed continuously, ddl may be hung(msg="refresh schema failed", ret=-4023, ret="OB_EAGAIN", refresh_count=3)

在排查时遇到刷新时的报错日志信息。

问题原因
出现这种现象的原因可能与 Schema 槽位已经被全部占满了有关,以下将简要介绍 Schema 槽位的概念,并说明如何排查问题是否由槽位导致。
提前知识
Schema 信息按租户划分,每个租户包含多个版本。每个版本记录了该租户在特定时间点的所有 Schema 信息,这些信息由多种类型的 Schema 对象组成,如表对象、数据库对象等。

Schema 槽位
负责管理由 Schema 主动刷新生成的 Schema 信息,维护多个版本的 Schema 信息。
具有固定数量的槽位(由租户级别配置项
_max_schema_slot_num控制,默认为 128 个),只有当某个版本的 Schema 信息被放置到槽位中时,才对其他模块可见。负责管理各个版本 Schema 信息的引用计数,新生成的 Schema 信息将被放置在空槽位中,或替换
schema_version最小的且未被引用的槽位。
错误码提示
当遇到错误码 OB_EAGAIN(-4023)时,可以考虑一下槽位已满的问题。
要判断 Schema 槽位是否已满,可以通过搜索日志中的
schema_mgr_item来确认。
如果打印出来这样的日志的个数达到
_max_schema_slot_num并且ref_cnt都不为 0,则表明槽位已经全部被占满了且无可淘汰槽位(具体哪个模块引用参考本文档 附录 这一节内容)。高版本可以查看表
__all_virtual_schema_slot查看对应租户的slot_id的个数和引用计数,来查看槽位是否被放满且没有可以淘汰的槽位。
槽位满
槽位满存在以下两种情况:
某些低版本的 Schema 信息被异常地持续引用,导致这些信息占用槽位且无法正常释放。由于这些 Schema 信息始终被引用,淘汰算法无法将其从槽位中移除,最终造成槽位被占满。
频繁执行 DDL 操作,产生大量 Schema 信息占用槽位。这些 Schema 信息都是被正常引用的,因此也会导致 Schema 槽位被占满。
为了确定具体是哪种情况,可以用下面的 SQL 在系统租户下执行以下查询,监控当前引用至少落后 2h 以上 Schema 版本的情况,此时可能有大事务、长 query,引用计数泄漏或者指定版本取 guard 的情况。
select a.*, b.latest_schema_version from (select * from __all_virtual_schema_slot where total_ref_cnt > 0) as a join (select tenant_id, max(refreshed_schema_version) as latest_schema_version from __all_virtual_server_schema_info group by tenant_id) as b where a.tenant_id = b.tenant_id and ((b.latest_schema_version - a.schema_version) / 1000 / 1000) >= 2 * 3600;

如果表里面的内容不为空,则表示有槽位被异常引用,可以通过 ref_info 来看引用的模块是哪个,去找相应的模块去询问(具体什么 Mod 去找哪个模块请参考本文档 附录 这一节内容)。如上图中信息显示 CACHED_GUARD 则表示有 session 持有 guard 导致槽位被异常占用。
对于SQL相关的模块,建议从以下几个方面进行排查:
对于外部模块的 SQL,这可以通过查询
__all_virtual_session_info和__all_virtual_processlist表来实现。select thread_id, time, state, svr_ip, svr_port, left(info,30), trace_id from __all_virtual_session_info where time > 1000 and id > 0 and state != 'SELLP' order by time desc;
过滤掉
state为SLEEP的记录,查看time值较大的信息,这些通常是长时间持有引用的 SQL。可以联系对应模块的 OceanBase 数据库技术支持人员确认具体情况。
此外,还可以通过日志来检查 session 的状态。如果持续出现
used_session_count不等于hold_session_count,可能意味着存在 session 泄漏问题。具体含义如下:used_session_count: 表示 session 分配器已分配的 session 数量。
hold_session_count: 表示 session mgr 中记录的 session 数量。
当这两个值相等时,说明所有分配出去的 session 都在 session mgr 中被正常管理。
$grep 'get current session count' observer.log.2022060611* observer.log.20220606110048:[2022-06-06 11:00:43.344867] INFO [SQL] ob_sql_session_mgr.cpp:487 [366332][0][Y0-0000000000000000-0-0] [lt=6] get current session count( used_session_count=1825, hold_session_count=1825, session_leak_count_threshold=100) observer.log.20220606110203:[2022-06-06 11:01:58.651943] INFO [SQL] ob_sql_session_mgr.cpp:487 [366332][0][Y0-0000000000000000-0-0] [lt=6] get current session count( used_session_count=1928, hold_session_count=1928, session_leak_count_threshold=100)
对于执行中的
inner_sql长时间持有引用的情况,由于这些inner_sql不会出现在__all_virtual_session_info和__all_virtual_processlist表中,因此需要通过 obstack 检查是否有线程 hang 住的情况(使用obstack _thread_id_生成堆栈信息,查看具体卡在哪个环节。同时,可以使用top -H命令检查线程的 CPU 占用率是否达到 100%,如果是,可能陷入了死循环)。以下是一些常见的长超时inner_sql情况:涉及
inner_sql双写隐藏表或隐藏分区的离线 DDL 操作,可以通过查询__all_virtual_ddl_task_status表来查看是否有长时间运行的任务。查看日志中
get_valid_duration_time相关的统计信息采集语句。
通过上面的方法来判定槽位满属于哪种情况,然后根据不同的情况去查看本文档 解决方法 这一节内容去解决。
问题的风险及影响
增量 Schema 刷新失败系统无法拿到最新的 Schema 信息,并且一直产生报错。
因为无效内存占用导致 500 租户 Schema 内存占用大且无法释放。
适用版本
OceanBase 数据库 V4.x 版本。
解决方法
发生泄漏
发现有无效占用的存在,请联系相关模块的 OceanBase 数据库技术支持人员进行检查。
正常情况
如果未发现槽位被无效占用,可能是因为频繁的 DDL 操作导致生成了大量 Schema 信息。在这种情况下,可以通过扩展槽位来解决问题,具体操作是通过租户级配置项 _max_schema_slot_num 增大 Schema 槽位数(默认值为 128)。
ALTER SYSTEM SET _max_schema_slot_num=150 TENANT=<tenant_name>;
调大槽位时需要注意以下几点,并了解相关的风险:
槽位数最多可设置为 256,建议在调大槽位前减少 DDL 次数,以防止槽位再次被占满。
调大槽位数可能会增加 Schema 模块占用的内存,需确保 OBServer 有足够的内存,以避免后续出现
OOM(Out of Memory)问题。若要调小槽位数,则需要重启 OBServer 才能生效。
规避方式
用户侧尽量减少频繁的做 DDL 的行为。
附录
下面为 mod_ref_cnt 中对应 MOD 和所负责的模块。
| mod_ref_cnt 中非 0 所在位置 | MOD | 负责的模块 |
|---|---|---|
| 0 | MOD_STACK | rs |
| 1 | MOD_VTABLE_SCAN_PARAM | sql |
| 2 | MOD_INNER_SQL_RESULT | sql |
| 3 | MOD_LOAD_DATA_IMPL | 现在还未使用 |
| 4 | MOD_PX_TASK_PROCESSS | sql |
| 5 | MOD_REMOTE_EXE | sql |
| 6 | MOD_CACHED_GUARD | session 相关 |
| 7 | MOD_UNIQ_CHECK | 存储 DDL |
| 8 | MOD_SSTABLE_SPLIT_CTX | 现在还未使用 |
| 9 | MOD_RELATIVE_TABLE | 存储 |
| 10 | MOD_VIRTUAL_TABLE | rs |
| 11 | MOD_DAS_CTX | 现在还未使用 |
| 12 | MOD_SCHEMA_RECORDER | 存储 |
| 13 | MOD_SPI_RESULT_SET | sql |
| 14 | MOD_PL_PREPARE_RESULT | pl |
| 15 | MOD_PARTITION_BALANCE | rs |
| 16 | MOD_RS_MAJOR_CHECK | 存储 |