
作者:李富强,爱可生 DBA 团队成员,OBCE;熟悉 MySQL,OceanBase 等数据库,擅长数据库架构设计,故障诊断,性能优化;技术让生活更美好,欢迎沟通交流,共同进步。
爱可生开源社区出品,原创内容未经授权不得随意使用,转载请联系小编并注明来源。
本文约 1500 字,预计阅读需要 8 分钟。

1. 背景
1.1 现象描述
用户反馈,应用访问 OceanBase 数据库时报错,报错内容为:ErrorCode = 4013, SQLState = HY001, Details = No memory or reach tenant memory limit
,重启 OBServer 后暂时恢复,但过一段时间又会报相同的错误。
1.2 环境信息
数据库版本:企业版 4.3.3.1 部署架构:2-2-2 租户模式:MySQL
2. 故障诊断
2.1 获取报错 SQL 的 TRACE_ID
开发同学通过 ODC[1] 执行查询也报同样错误,在 ODC 的执行记录中可以直接看到 SQL 的 TRACE_ID 信息。

点击异常 SQL 的 TRACE_ID,会弹出如下界面,依次点击全链路诊断,鼠标移动到com_query_process
右侧的蓝色耗时区域,会出现该 Span 的具体内容,其中节点字段,即为 SVR_IP(如下是本地环境举例说明)。

获取执行过的 SQL 或者黑屏执行的 SQL 的 TRACE_ID 和 SVR_IP 方法,请查看文末参考资料[2]。
2.2 根据 TRACE_ID 过滤详细日志
根据报错的时间范围和 TRACE_ID,过滤 OBServer 日志,在 SVR_IP 节点(实际为:113 节点)过滤到了详细的报错内容,显示租户占用内存加上租户新申请内存超过租户内存限制(tenant_hold+alloc_size>tenant_limit
)。
#[OOPS]: alloc failed reason is that tenant memory has reached the upper limit(tenant_id: 1010, tenant_hold: 11595448320, tenant_limit: 11596411700, alloc_size: 2097152)
2.3 查看租户中各个上下文内存的占用
过滤 113 节点 的 OBServer 日志,查看 1010 租户 的各个上下文的内存使用,显示上下文 DEFAULT_CTX_ID
的内存占用最大,接近租户的内存使用限制了。
# grep 'malloc_allocator.*tenant: 1010' observer.log.20250522105401599 -A 20

2.4 查看指定上下文中各个 mod 内存的占用
过滤 113 节点 的 OBServer 日志,查看 1010 租户 的上下文 DEFAULT_CTX_ID中各个mod的内存使用,显示 SqlSessionSbloc 这个 mod 占用内存最大,几乎用了整个租户 80% 以上的内存。通过一段时间观测内存使用会持续增加,最终导致租户内存用满。
分析到这,我们已经定位到导致租户内存用满的 mod 是谁了。通过查阅官方文档,并没有看到对这个 mod 的介绍,但我们知道,从 4.1 版本开始,提供了虚拟表来记录 mod 内存分配的相关的关键信息,请继续往下看。
grep '1010 ctx_id= DEFAULT_CTX_ID' observer.log.20250522105401599 -A 10

2.5 诊断内存模块内存消耗持续增长的问题
从 OceanBase 4.1.0 版本开始,提供了 __all_virtual_malloc_sample_info 虚拟表[3],此虚拟表常态化记录内存分配相关的关键信息,当 OBServer 的某个模块分配内存超过 GB,基于此虚拟表的信息,通常有能力定位到问题的根因。
有关 __all_virtual_malloc_sample_info
表的字段说明如下:
2.6 查询指定 mod 的内存分配信息
注意:虚拟表
__all_virtual_malloc_sample_info
只能在sys
租户和 x86_64 系统架构下查询。
select * from oceanbase.__all_virtual_malloc_sample_info where mod_name='SqlSessionSbloc' order by alloc_bytes DESC ;

2.7 将采集到的分配内存的 back trace 转换为堆栈
# addr2line -pCfe bin/observer 0x1f5951a0 0x788cfac 0x7ce43e0 0x7ce3a48 0x15111873 Oxf35e3e8 0x79304ee 0x792e9af 0x791e66b 0xf054727 Ox1f6509be

通过调用栈分析,直接调用内存分配的函数是 set_login_info
,怀疑内存泄漏发生在 oceanbase::sql::ObSQLSessionInfo::set_login_info
函数中。
2.8 查看代码进一步确认
OceanBase 社区版是 MySQL 租户兼容模式,所以我们可以看开源代码进行问题确认。
打开 OceanBase 社区版[4],选择最新发布版本,通过 ObSQLSessionInfo 关键字,找到代码文件ob_sql_session_info.cpp
,点击进入。

切换查看方式为 Blame,通过关键字 set_login_info,搜索函数的代码变更记录。

打开包含 fix memory leak 的代码提交的链接,可以看到该 commit 修复了一个由于 OceanBase 内部在复用一个会话时,直接分配内存(而不是尝试复用已有的内存),导致了内存泄露问题(fix memory leak caused by calling set_login_info() when reusing a session
),并且可以看到该 Bug 在 4.3.5 及之后的版本被修复了。
PS:如果这里没有查到函数的 bug fixed,需要研发根据
set_login_info
函数代码逻辑进一步分析内存泄露的原因。

2.9 故障解决
最后和研发团队确认后,对当前数据库版本进行了升级(建议升级到 v4.3.5)。
升级后,在业务租户进行查询(租户中内存占用排名靠前的 mod 中,找不到 SqlSessionSbloc 这个 mod 了),升级后租户内存爆的问题解决。
SELECT svr_ip,ctx_name,mod_name ,sum(hold) FROM oceanbase.__all_virtual_memory_info WHERE tenant_id = 1010 group by svr_ip,ctx_name,mod_name order by sum(hold) desc limit 10;

3. 总结
本文主要分享了当租户内存用满时,如何通过分析 OBServer 日志,找到引起租户内存用满的 mod,遇到 mod 的内存占用持续增长的情况,如何进一步结合代码进行问题根因的定位,希望对你有帮助。
开发者工具 ODC: https://www.oceanbase.com/product/odc
[2]获取 SQL 执行的 svr_ip、trace_id 的方法: https://www.oceanbase.com/knowledge-base/oceanbase-database-1000000000225641
[3]使用 __all_virtual_malloc_sample_info 诊断 OceanBase 数据库内存模块问题: https://www.oceanbase.com/knowledge-base/oceanbase-database-1000000002393827
[4]社区版 Github 仓库地址: https://github.com/oceanbase/oceanbase/tree/v4.3.5_CE_BP2_HF1
本文关键字:#OceanBase #故障分析


✨ Github:https://github.com/actiontech/sqle
📚 文档:https://actiontech.github.io/sqle-docs/
💻 官网:https://opensource.actionsky.com/sqle/
👥 微信群:请添加小助手加入 ActionOpenSource
🔗 商业支持:https://www.actionsky.com/sqle





