OceanBase -4013租户内存超限日志分析培训案例
从未知 SQL 起步 · 全链路日志 · 内存模板溯源
OceanBase Database 3.2.3 专版 · 修订版
| 文档版本 |
v1.1(相对 v1.0:纠正“上帝视角过滤”,补全链路日志) |
| 发布日期 |
2026年07月31日 |
| 目标版本 |
OceanBase Database 3.2.3 / OBProxy |
| 适用对象 |
值班 DBA / 中高级数据库运维工程师 |
| 案例环境 |
集群 obv3 · 节点 172.16.104.28(observer06) |
| 值班已知信息 |
约 23:10 出现 ORA-00600/-4013(不知具体 SQL) |
| 结论摘要 |
SQL_EXEC_CTX_ID(ctx_id=5)占满租户 4GB 上限 |
| Proxy 日志 |
/home/admin/obproxy/log |
| Observer 日志 |
/home/admin/oceanbase/log |
| 密级 |
内部资料 |
一、值班起始态势(你真正知道什么)
本案例按真实值班建模。假设业务/监控只给了如下信息,除此之外一无所知:
| 已知项 |
取值 |
未知项(待日志发现) |
| 报错现象 |
ORA-00600 / -4013,提示 No memory or reach tenant memory limit |
具体 SQL 文本 |
| 大致时间 |
2026-07-30 23:10 前后 |
用户名、租户名(若工单未写) |
| 接入点 |
经 OBProxy 访问(端口 2883 一类) |
落到哪台 Observer |
| 环境 |
集群 obv3,节点可登录 172.16.104.28 |
内存模板 / ctx_id |
因此分析原则是:先宽后窄——用时间/错误码捞出失败请求,再从日志字段里“读出”租户、用户、SQL、Trace、后端地址,最后用 Trace 到 Observer 定位内存模板。
二、日志目录与各文件作用
2.1 OBProxy(/home/admin/obproxy/log)
| 日志文件 |
作用 |
本案例优先级 |
| obproxy_error.log |
失败请求总账:时间、集群/租户/用户、SQL、错误码、错误信息、耗时、客户端、后端 Observer、Trace ID |
P0 首查 |
| obproxy_digest.log |
请求摘要(含失败),可交叉核对耗时与路由 |
P1 佐证 |
| obproxy_slow.log |
慢请求;大对象/长 PL 常出现 |
P1 佐证 |
| obproxy.log |
主运行日志(连接/会话/路由),体量大、常滚动 |
P2 深挖会话 |
| obproxy_stat.log |
统计类 |
P3 |
| obproxy_diagnosis.log |
诊断类 |
P3 |
| obproxy_xflush.log |
高频刷盘/监控,体积最大 |
一般不用于单 SQL 根因 |
| obproxy_trace / pool / limit |
细 Trace、连接池、限流(本环境常为空) |
按需 |
2.2 Observer(/home/admin/oceanbase/log)
| 日志文件 |
作用 |
本案例优先级 |
| observer.log / observer.log.YYYYMMDDHHMMSS |
执行端全量日志;注意滚动归档 |
P0(按 Trace 查归档) |
| observer.log.wf |
WARN/ERROR/FATAL 过滤,体积更小 |
P0 快速扫 ERROR |
| rootservice.log(.wf) |
RS 元数据/均衡 |
本案例无关 |
| election.log |
选举 |
本案例无关 |
| obesi-daemon.log |
守护进程 |
本案例无关 |
【滚动提醒】
本案例故障在 23:10,当前 observer.log 可能已从更晚时间重开写。实际命中文件:observer.log.20260730231446(覆盖约 23:08~23:14)。
三、实战排查步骤(从未知 SQL 起步)
步骤 A:进 Proxy 错误日志——只用时间窗
是的,入口就是 obproxy_error.log。正确首查示例:
A1 首查命令(推荐)
cd /home/admin/obproxy/log
正确:只按时间窗(值班已知“大约 23:10”)
grep “2026-07-30 23:1” obproxy_error.log
若时间窗内噪声多,再用错误特征收窄(仍不知道 SQL 名)
grep “2026-07-30 23:1” obproxy_error.log | grep -E “4013|No memory|tenant memory|ORA-00600”
本案例时间窗内命中 1 条失败请求(字段已由 Proxy 写全):
【链路日志 ①】obproxy_error.log(发现 SQL/用户/Trace/后端)
2026-07-30 23:10:03.368589,obv3,obv3:oboracle:JAMES,OB_ORACLE,OB_MYSQL_COM_QUERY,OTHERS,failed,600,
BEGIN%0A test_clob(52428800);%0AEND;,15879817us,0us,0us,15878636us,
Y0-00007F0070CFF760,YB42AC10681C-000657B5B185F478-0-0,
172.16.104.22:15296,0,172.16.104.28:2881,
ORA-00600: internal error code, arguments: -4013, No memory or reach tenant memory limit,
YB42AC10681C-000657B5B185F478-0-0
步骤 B:从 error 行“读出”后续检索条件
到这一步,SQL 名、用户名才第一次进入你的知识集。把它们整理成工作表:
| 字段 |
从 error 日志读出的值 |
后续用途 |
| 时间 |
2026-07-30 23:10:03.368589 |
对齐 Observer 时间 |
| 集群/租户/用户 |
obv3 / oboracle / JAMES |
确认业务归属 |
| SQL |
BEGIN test_clob(52428800); END; |
根因关联业务写法 |
| 错误 |
600 / ORA-00600 / -4013 |
与客户端一致 |
| 耗时 |
15879817 us ≈ 15.88 s |
可对照 slow |
| 客户端 |
172.16.104.22:15296 |
回溯来源 |
| 后端 Observer |
172.16.104.28:2881 |
决定上哪台机器查 observer.log |
| Trace ID |
YB42AC10681C-000657B5B185F478-0-0 |
Observer 全链路主键 |
【关键认知】
test_clob、JAMES 不是“过滤条件的来源”,而是“error 日志的产物”。之后若再 grep test_clob,那是二次精确检索,不是首查。
步骤 C:同机 Proxy 侧交叉佐证(可选)
此时已有 Trace,可用 Trace 去 digest/slow 做交叉确认(仍不必猜 SQL):
A2 用 Trace 交叉(已从 error 获得)
TRACE=YB42AC10681C-000657B5B185F478
grep “$TRACE” obproxy_digest.log
grep “$TRACE” obproxy_slow.log
【链路日志 ②】obproxy_digest.log
2026-07-30 23:10:03.368578,obv3,obv3:oboracle:JAMES,OB_ORACLE,OB_MYSQL_COM_QUERY,OTHERS,failed,600,
BEGIN%0A test_clob(52428800);%0AEND;,15879817us,…,YB42AC10681C-000657B5B185F478-0-0,
…,172.16.104.28:2881
【链路日志 ③】obproxy_slow.log(长耗时失败,落入慢日志)
2026-07-30 23:10:03.368585,obv3,obv3:oboracle:JAMES,OB_ORACLE,OB_MYSQL_COM_QUERY,OTHERS,failed,600,
BEGIN%0A test_clob(52428800);%0AEND;,15879817us,…,YB42AC10681C-000657B5B185F478-0-0,
…,172.16.104.28:2881
步骤 D:切到后端 Observer,按 Trace 深挖
D1 Observer 首查
后端已从 error 得知:172.16.104.28:2881
ssh root@172.16.104.28
cd /home/admin/oceanbase/log
找覆盖 23:10 的滚动文件
ls -lrt observer.log.2026073023*
本案例命中:
observer.log.20260730231446
只用 Trace(不要猜 SQL)
grep “B185F478” observer.log.20260730231446 | grep -E “ERROR|WARN|alloc|4013|SQL_EXEC”
步骤 E:读出内存模板 SQL_EXEC_CTX_ID
同一 Trace 下,分配失败链如下(按时间顺序):
四、全链路关键日志清单(按时间)
以下为本案例从“内存触顶”到“返回客户端”的完整关键日志。培训时建议按序号逐条讲解。
4.1 触顶前兆(租户已接近 4GB)
④ 租户内存高压(故障前约 1 秒)
【链路日志 ④】observer.log.20260730231446
[2026-07-30 23:10:02.845840] INFO [COMMON] ob_tenant_mgr.cpp:1577
A minor freeze is needed(
mem_tenant_limit=4294967296, # 4GB
mem_tenant_hold=4206886912, # ≈3.92GB,已很高
tenant_id=1002)
4.2 分配失败主链(同一 Trace)
⑤~⑱ Observer 同 Trace 关键链
Trace = YB42AC10681C-000657B5B185F478-0-0
【⑤】23:10:03.333462 ERROR [COMMON] sync_wash_mbs (ob_kvcache_store.cpp:449)
can not find enough memory block to wash(ret=-4273, size_washed=0, size_need_washed=14680064)
→ 想从 KVCache 刷出约 14MB 失败
【⑥】23:10:03.337058 WARN [COMMON] sync_wash_mbs (ob_kv_storecache.cpp:793)
sync_wash_mbs failed(ret=-4273, tenant_id=1002, wash_size=14680064, wash_single_mb=false)
【⑦】23:10:03.337066 WARN [COMMON] alloc_chunk (ob_resource_mgr.cpp:121)
sync_wash_mbs failed(ret=-4273, tenant_id=1002, wash_size=14680064, wash_single_mb=false)
【⑧】23:10:03.337075 WARN alloc_block (ob_page_manager.h:199)
oops, alloc failed, tenant_id=1002 ctx_id=5 hold=3523215360 limit=9223372036854775807
【⑨】23:10:03.337082 WARN alloc (ob_allocator_v2.cpp:51)
[OOPS] alloc failed reason: tenant memory has reached the upper limit
(tenant_id: 1002, tenant_hold: 4290772992, tenant_limit: 4294967296, alloc_size: 10485760)
→ 根因定性:租户上限触顶(不是 ctx 单独限流)
【⑩】23:10:03.337095 WARN alloc (ob_allocator_v2.cpp:56)
oops, alloc failed, tenant_id=1002, ctx_id=5, ctx_name=SQL_EXEC_CTX_ID,
ctx_hold=3523215360, ctx_limit=9223372036854775807,
tenant_hold=4290772992, tenant_limit=4294967296
→ ★ 内存模板答案:SQL_EXEC_CTX_ID(ctx_id=5),hold≈3.28GB
【⑪】23:10:03.337109 WARN alloc_new_page (page_arena.h:334)
cannot allocate memory.sz=9731831, pages_=562, total_=2949748480
→ 本会话 page arena 已堆积约 2.75GB
【⑫】23:10:03.337118 ERROR [PL] calc_write_result (ob_dbms_lob.cpp:696)
alloc memory failed(ret=-4013, res_len=9731799)
→ PL/DBMS_LOB 层暴露 -4013
【⑬】23:10:03.337174 WARN [PL] write (ob_dbms_lob.cpp:784)
fail to calc(ret=-4013)
【⑭】23:10:03.337236 WARN [PL] writeappend (ob_dbms_lob.cpp:1080)
fail to calc dbms_lob.write procedure(ret=-4013, … payload_size:9699032 …)
→ 业务动作:DBMS_LOB.WRITEAPPEND;当时临时 LOB 逻辑大小仅约 9.3MB
【⑮】23:10:03.367758 WARN [PL] execute (ob_pl.cpp:2632)
Unhandled exception has occurred in PL(*ctx_.status_=-4013, ret=-4013)
【⑯】23:10:03.367973 WARN [SERVER] response_result (ob_sync_cmd_driver.cpp:118)
close result set fail(cret=-4013)
【⑰】23:10:03.368037 WARN [SERVER] response_result (ob_sync_cmd_driver.cpp:124)
result set open failed, check if need retry(ret=-4013, cli_ret=-4013, retry_ctrl_.need_retry()=0)
【⑱】23:10:03.368123 WARN [SERVER] do_process (obmp_query.cpp:735)
execute query fail(ret=-4013, …)
→ Observer 向 Proxy/客户端返回失败
4.3 回到 Proxy:错误返回客户端
Observer 在 23:10:03.368123 判定失败后,Proxy 在约 23:10:03.368589 写入 error/digest/slow(即链路日志 ①②③)。客户端看到 ORA-00600 / -4013。
4.4 链路总览图
因果方向(自下而上发生,自上而下呈现给 DBA)
客户端 ORA-00600/-4013
↑
obproxy_error / digest / slow ←①②③ (发现 SQL、用户、Trace、后端)
↑
Observer 返回失败 ←⑱
↑
PL 未处理异常 / WRITEAPPEND ←⑫⑬⑭⑮
↑
alloc 失败 + 打出 ctx_name ←⑧⑨⑩⑪ ★ SQL_EXEC_CTX_ID
↑
sync_wash 失败 (-4273) ←⑤⑥⑦
↑
租户内存已高压 ←④
↑
业务:循环 DBMS_LOB.WRITEAPPEND 构造大临时 CLOB
五、根本原因与数字结论
【根本原因】
租户 oboracle(tenant_id=1002)在 172.16.104.28 的 Unit 内存上限为 4GB。PL 过程通过 DBMS_LOB.WRITEAPPEND 循环扩容临时 CLOB,内存记在 SQL_EXEC_CTX_ID(ctx_id=5)。执行期 Arena 等机制使旧缓冲延迟回收,SQL_EXEC_CTX 占用飙至约 3.28GB,租户 hold 逼近 4GB;再申请约 10MB 时 wash 失败,返回 -4013。
注意:失败时 LOB 逻辑大小仅约 9.3MB,远小于 4GB——是执行期内存放大,不是“50MB 对象本身超限”。
| 项目 |
值 |
| 内存模板 |
SQL_EXEC_CTX_ID(ctx_id=5) |
| tenant_limit |
4294967296(4GB) |
| tenant_hold(失败瞬间) |
4290772992(≈3.996GB) |
| SQL_EXEC_CTX hold |
3523215360(≈3.28GB) |
| 本次申请 |
alloc_size=10485760;res_len≈9731799 |
| Unit 配置名 |
config_oboracle_zone1_u2c4g_knb |
| Trace ID |
YB42AC10681C-000657B5B185F478-0-0 |
六、处置建议(简)
• 紧急:确认会话结束/必要时杀会话,观察租户内存回落。
• 规避:禁止单会话内用 WRITEAPPEND 堆超大临时 LOB;改为分批/流式写入。
• 容量:短期可扩 unit memory,但必须同步改写法,否则只是推迟触顶。
七、培训考核(强调方法论)
| 考题 |
合格标准 |
| 首查该用哪类过滤条件? |
时间窗和/或 4013/No memory;不能一上来用 SQL 名/用户名 |
| test_clob、JAMES 从哪来? |
从 obproxy_error.log 字段读出,不是先验知识 |
| 如何定位到哪台机器? |
error 日志中的后端地址 172.16.104.28:2881 |
| Observer 用什么做主键? |
Trace ID(B185F478…) |
| 哪个内存模板? |
SQL_EXEC_CTX_ID / ctx_id=5 |
| 触顶的是什么? |
tenant_limit(4GB),不是 ctx_limit |
| 举出至少 5 条链路关键日志 |
能覆盖 wash→alloc→ctx_name→dbms_lob→proxy error |
【考官标准答案卡】
入口文件:obproxy_error.log
首查:grep 时间窗(可选再 grep 4013/No memory)→ 读 Trace/后端/SQL/用户
深挖:observer.log.20260730231446 + Trace
模板:SQL_EXEC_CTX_ID(ctx_id=5)
根因:租户 4GB 上限触顶 + WRITEAPPEND 导致 SQL_EXEC_CTX 暴涨