本文介绍 4013 内存爆问题的排查。
内存爆的类型
内存爆主要分为五类,可以通过关键词 OOPS
确定内存爆的类型。
内存爆的类型 | 日志信息(关键字为 [OOPS]) |
---|---|
SINGLE_ALLOC_SIZE_OVERFLOW | single alloc size large than 4G is not allowed(alloc_size: %ld) |
CTX_HOLD_REACH_LIMIT | ctx memory has reached the upper limit(ctx_name: %s, ctx_hold: %ld, ctx_limit: %ld, alloc_size: %ld) |
TENANT_HOLD_REACH_LIMIT | tenant memory has reached the upper limit(tenant_id: %lu, tenant_hold: %ld, tenant_limit: %ld, alloc_size: %ld) |
SERVER_HOLD_REACH_LIMIT | server memory has reached the upper limit(server_hold: %ld, server_limit: %ld, alloc_size: %ld) |
PHYSICAL_MEMORY_EXHAUST | physical memory exhausted(os_total: %ld, os_available: %ld, server_hold: %ld, errno: %d, alloc_size: %ld) |
可以在内存爆的时间点(比如 20230730142424792
),搜索 OOPS
日志,确定属于哪个类型的内存爆。
grep '\[OOPS\]' observer.log.20230730142424792
租户内存爆
检查是否存在 msg
信息,表示特定租户的内存爆。
此处 msg
表示 1008 租户的内存爆了,需要进一步查看该租户下哪个 CTX_ID
的占用偏高。
1.1 分析租户的内存元信息,确定高内存占用的 CTX_ID
-
分析租户的内存元信息,确定高内存占用的
CTX_ID
。通过关键词
malloc_allocator.*tenant: 1008
可以获取租户的内存元信息(limit
、hold
、cache_hold
),-A 可以显示更多的内存元信息的细节,包括该租户下各 CTX 的内存占用,以及租户线程的 PM 信息。上述内存元信息显示
DEFAULT_CTX_ID
、DEFAULT_CTX_ID
、RPC_CTX_ID
占用比较高,需要进一步分析这些CTX_ID
下占用最高的几个内存模块。 -
CTX 的内存元信息分析
查找和分析
DEFAULT_CTX_ID
、DEFAULT_CTX_ID
、RPC_CTX_ID
的内存元信息。以
DEFAULT_CTX_ID
举例 。通过关键词
1008 ctx_id= DEFAULT_CTX_ID
可以获取 1008 租户的DEFAULT_CTX_ID
的内存元信息(hold
、used
、limit
、内存碎片清理信息),-A 可以显示该CTX_ID
的各内存模块的内存占用。发现疑似导致内存爆的模块后,找模块owner
进行最后的确定。CTX_ID
的内存元信息中used
与hold
相差很大。存在二种可能(内存碎片、MEMORY 日志统计延时),可以通过查看内存爆所在的多个时刻的
CTX_ID
的内存元信息确定。-
内存碎片。
内存爆触发内存碎片清理机制,
washed_size
会显著变大,hold
会减小到与used
差不多大。 -
MEMORY 日志统计延时。
-
washed_size
变化幅度很小,可能会出现used
大于hold
的现象。一般是由内存快速膨胀导致的,需要找到内存膨胀模块。 -
在内存爆所在时间段,搜索
CTX_ID
的used
明显比较大的 MEMORY 日志,跟前后时刻的 MEMORY 日志进行比对,内存占用增幅最大的模块很可能就是内存膨胀模块。 -
如果找不到
used
较大的 MEMROY 日志,需要等待复现(用脚本定时打印该CTX_ID
中占用最高的几个内存模块,并且输出alloc_bytes
最大的堆栈。
-
当无明显占用较大的内存模块的情况
-
确定占用最高的几个内存模块是否有已知内存问题未修复。
-
占用最高的几个内存模块,是否有陌生的模块。
有明显占用较大的内存模块。
直接锁定占用最高的内存模块。
-
-
kvcache 内存异常。
未发现占用较大的 CTX_ID
,并且租户的大部分内存都是 kvcache,说明租户内存不足时挤占 kvcache 的内存失败了,需要确定失败的原因。
-
搜索关键词
T1008.*tenant sync wash failed, cache memblock info
,其中ref_count
不为 2 表示这块内存被应用层 hold 住了,status
不为 2 表示这块内存正在被 cache 使用。如果存在ref_count
和status
都为 2 的内存,则说明存在 bug,需要存储方面的分析。 -
搜索关键词
tenant_id.*1008 cache memory info
,发现cache_size
占用最大的模块,找 owner 分析占用是否符合预期。
OBServer 内存爆
-
一般是500租户超卖导致的,可以先获取 500 租户的内存元信息,按照 租户内存爆 这一节的步骤来确定导致 OBServer 内存爆的模块。
-
搜索关键词
CHUNK_MGR
,确定 OBServer 是否存在大量缓存,若存在大量缓存则可能是 OBServer 缓存未设上限,导致 OBServer hold 的内存接近memory_limit
,此时申请大于 4M 的内存会失败。可以通过设置配置项memory_chunk_cache_size
释放缓存。 -
500 租户的内存占用符合预期,查看全部业务租户的内存元信息。
-
全部业务租户的总
limit
超过了memory_limit - system_memory
,很可能是租户扩缩容时存在 BUG,或者其他问题。 -
全部业务租户的总
limit
符合预期,可能是业务租户的规格设置过大,可以适当调小快速恢复业务。
-
CTX_ID 内存爆
按照 CTX 的内存元信息分析 这一节的步骤来确定导致 OBServer 内存爆的模块。
物理内存耗尽
常见原因是超卖机器的物理内存,一台机器上部署多个 OBServer,或者 OBServer 的 memory_limit
接近机器的物理内存。可以结合 tsar --mem
在内存爆时刻的日志进行验证。
无 OOPS 日志
-
通过
CHUNK_MGR
日志确定是否是 OBServer 内存爆,若是 OBServer 内存爆则按照 OBServer 内存爆 这一节来排查问题。 -
通过全部租户的内存元信息确定是否是租户内存爆,若是,则按照 租户内存爆 这一节来排查问题。
适用版本
OceanBase 数据库所有版本。