首批通过分布式安全可靠测评,为关键业务系统打造
租户 MemStore 内存满问题排查
更新时间:2026-07-01 12:35:30
本文介绍了租户 MemStore 内存满问题的排查方法以及常见场景。
问题描述
OBServer MemStore 内存主要用于保存数据库租户增量数据。当 MemStore 占用的空间 total_memstore_used 增长到 memstore_limit,则会出现 MemStore 内存满导致无法继续写入的情况。此时需要对该问题进行排查,通过定位问题原因来解决问题。
问题排查方法
排查 MemStore 内存满的问题,一般从以下几个方向入手:冻结、转储、MemStore 释放。
检查冻结情况。
首先查询
__all_virtual_tenant_memstore_info表的active_memstore_used列确定冻结是否完成。如果
active_memstore_used的值超过major_freeze_trigger,则说明冻结没有正常执行,需要根据以下步骤进一步检查。确认冻结触发线程。
如果冻结没有正常执行,首先检查触发冻结的线程是否正常工作,搜索关键日志
====== tenant manager timer task ======是否周期性打印。如果上述日志没有周期性打印,则说明线程卡住,需要搜索线程日志或 pstack 查看超时的原因。
[admin@hostname log]$ grep "tenant manager timer task" observer.log确认冻结失败的原因。
线程正常的情况下说明是有个别分区无法冻结,这种情况可以通过以下 SQL 查询。
查看没有冻结的 MemStore,按照 pkey 查找日志继续定位问题,也可以直接搜索冻结失败的日志。
obclient> SELECT * FROM __all_virtual_tenant_memstore_allocator_info WHERE svr_ip='xxx.xxx.xx.xxx' AND tenant_id=xxx AND mt_is_frozen=0 ORDER BY mt_protection_clock limit 10;
检查转储情况。
如果上述步骤中,
total_memstore_used的值大于major_freeze_trigger的值,且active_memstore_used的值小于major_freeze_trigger的值,则说明有冻结的 MemStore 没有释放,需要先确认冻结的 MemStore 是否完成了转储。确定未释放的 MemStore。
通过以下 SQL 检查未释放的 MemStore 的 pkey。
obclient> SELECT * FROM __all_virtual_tenant_memstore_allocator_info WHERE svr_ip='xxx.xxx.xx.xxx' AND tenant_id=xxx and mt_is_frozen=1 ORDER BY mt_protection_clock limit 10;通过查询到 pkey 查询
__all_virtual_table_mgr,继续分析有 MemStore 不释放的原因。可以通过如下查询快速定位未释放的 MemTable。
查看有活跃事务或者引用计数不为
0或者备机读时间戳小于快照点的 MemTable。obclient> SELECT * FROM (SELECT A.svr_ip, A.table_id, A.partition_id, A.is_active, A.table_type, A.ref, A.write_ref, A.trx_count, A.base_version, A.multi_version_start, A.snapshot_version, B.min_log_service_ts, B.min_trans_service_ts, B.min_replay_engine_ts FROM (SELECT * FROM __all_virtual_table_mgr WHERE table_type=0 AND is_active =0 ) AS A join (SELECT * FROM __all_virtual_partition_info WHERE svr_ip='xxx.xxx.xx.xxx') AS B ON A.table_id = B.table_id AND A.partition_id=B.partition_idx AND A.svr_ip =B.svr_ip) AS C WHERE C.write_ref >0 OR C.trx_count >0 OR (C.snapshot_version > least(least(min_trans_service_ts, min_replay_engine_ts), min_log_service_ts));查看上一查询得到的 MemTable 对应的分布区域的日志回收进度,确定是否存在日志回放不及时。
obclient> SELECT * FROM __all_virtual_partition_replay_status WHERE (table_id, partition_idx) IN (SELECT table_id, partition_id FROM (SELECT A.svr_ip, A.table_id, A.partition_id, A.is_active, A.table_type, A.ref, A.write_ref, A.trx_count, A.base_version, A.multi_version_start, A.snapshot_version, B.min_log_service_ts, B.min_trans_service_ts, B.min_replay_engine_ts FROM (SELECT * FROM __all_virtual_table_mgr WHERE table_type=0 AND is_active =0 ) AS A JOIN (SELECT * FROM __all_virtual_partition_info WHERE svr_ip="xxx.xxx.xx.xx") AS B ON a.table_id = b.table_id AND A.partition_id=B.partition_idx AND A.svr_ip =B.svr_ip) AS C WHERE C.write_ref >0 or C.trx_count >0 OR (C.snapshot_version > least(least(min_trans_service_ts, min_replay_engine_ts), min_log_service_ts))) AND svr_ip='xxx.xxx.xx.xxx';
确认转储调度。
如果是 MemStore 没有任何对应的 SSTable 生成,则有可能是 MemStore 没有满足调度条件:
备集群读时间戳落后 MemStore 的
snapshot_version。MemStore 上仍有活跃事务(
trx_count不为零)。注意
trx_count不为零的情况只有 v2.2.x 版本有影响,v3.x 版本无需关注此字段。
其中,MemStore 的
snapshot_version可以从__all_virtual_table_mgr中获得。obclient> SELECT * FROM __all_virtual_table_mgr WHERE svr_ip='xxx.xxx.xx.xxx' AND svr_port=xxx AND table_id=xxx AND partition_id=xxx;此外,备机读时间戳落后的原因常见的有无主或 rebuild 调度慢或执行失败,未结束的事务可以通过查询
__all_virtual_trans_stat表,并通过日志进一步分析。备机读时间戳为___all_virtual_partition_info中min_log_service_ts、min_trans_service_ts与min_replay_engine_ts三者中的最小值。obclient> SELECT * FROM __all_virtual_partition_info WHERE svr_ip='xxx.xxx.xx.xxx' AND svr_port=xxx AND table_id=xxx AND partition_idx=xxx;;确认转储过程。
如果调度转储条件已经满足,则说明转储本身没有执行或执行失败,可以通过转储的关键日志确认执行流程。注意下面脚本中的 pkey 需要替换成实际的 pkey 字段。
add dag success.*pkey task start process.*pkey task finish process.*pkey dag finish.*pkey
检查 MemStore 释放情况。
当 MemStore 对包含的主表和索引表全部转储完成后,可以通过搜索关键日志
succeed to release memtable.*pkey,如果确认存在这条日志,则问题出在 MemStore 最后的释放阶段,可以通过查询__all_virtual_table_mgr的ref列确认未释放 MemStore 的引用计数是否发生泄漏。obclient> SELECT table_id, partition_id, base_version, snapshot_version FROM __all_virtual_table_mgr WHERE svr_ip='xxx.xxx.xx.xxx' AND table_type=0 except SELECT table_id, partition_idx, base_version, snapshot_version FROM __all_virtual_memstore_info WHERE svr_ip='xxx.xxx.xx.xxx';
常见场景
OceanBase V2.x 版本使用大事务场景
OceanBase V2.x 版本为解决V1.X版本冻结会终止事务的问题,引入数据搬迁机制,即 MemStore 在冻结过程中,会确定冻结版本号 Frozen Version,凡是commit_version 大于 Frozen Version 的事务,都需要将其写入的数据搬迁到新创建的 Active MemStore 上。因此在 V2.x 上存在写事务过大且并发量比较高的场景时,会相对容易出现 MemStore 内存高甚至用完的问题。V3.x 版本优化了该机制,从根本上解决了未提交的写大事务可能造成的 MemStore 用尽的问题。
因此当您使用的是 OceanBase V2.x 版本时,遇到 MemStore 用尽的问题,请排查是否存在高并发的大事务。解决思路如下:
控制事务大小和客户端并发量。
修改隐藏配置项
_max_trx_size。该配置项为当前事务单个分区最大允许写入的事务大小,V2.x 版本不建议业务调大。如果必须要调大,请联系 OceanBase 技术支持人员。这里介绍下如何计算事务大小计算公式:
insert 最大允许插入的行数:_max_trx_zie /(所有插入的列的长度总和 * 2) update 最大允许更新的行数:_max_trx_size /((被更新列长度总和)*4)示例:假设
_max_trx_size为默认值 100M。现在数据库创建一张表,create table test (col1 varchar(512),col2 varchar(512)) ;此时 insert 场景允许的行数为100,000,000/((512+512)*2)= 50,000行;update 场景(假设语句为update test set col1=<new value >, col2=<new value>)允许的行数为100,000,000/((512+512)*4)=25,000行。
写入的速度大于转储的速度,导致 MemStore 耗尽
在一些特定的场景下,例如大量高并发的数据导入,MemStore 的写操作的速度过快,系统还来不及转储,MemStore 就被用完,返回用户报错 4030 (Over tenant memory limits)。此时您可以通过尝试提高转储速度或减缓写入速度来解决这类问题。
调快转储速度
当转储慢并不是因为基础设施(CPU、IO等)因素造成的瓶颈,那么可以通过以下参数来控制转储发生的时机以及转储的线程个数来调快转储的速度。
freeze_trigger_percentage:MemStore 使用比例达到阀值触发冻结。minor_merge_concurrency:转储工作线程数。mini_merge_concurrency:L0 转储工作线程数。更详细的参数配置情况,请参见 《 参考指南(MySQL 模式) 》和 参考指南(ORACLE 模式) 。
注意
综合调整转储有可能会带来比较大的变化,请务必在测试环境上调整达到预期结果后再考虑应用在生产环境。
在生产环境请采取逐步微调的方法来随时观察调整之后的转储速度以及带来的副作用。在适度提高转储速度之后,可以观察问题是否缓解。
减缓 MemStore 写入速度。
您可以使用 MemStore 写入限速功能来处理该问题。OceanBase MemStore 写入限速的工作原理是:当租户的 MemStore 内存已使用量达到 MemStore 可使用内存量的 90%(通过配置项
writing_throttling_trigger_percentage可调)时,即触发写入限速,此后的内存分配受限。通过增加 query 的运行时间(response time), 从而降低外部的写入速度。 当 MemStore 的内存恢复到限速水位之下,OceanBase 会周期性地检查并关闭掉 MemStore 写入限速,使得 MemStore 可用内存容量恢复后,增量数据的处理能力也能及时恢复。OB 的 MemStore 写入限速是通过以下两个参数进行控制和调整。
writing_throttling_trigger_percentage:控制写入限速的触发。writing_throttling_maximum_duration:触发写入限速后,剩余 MemStore 的内存量预期在 writing_throttling_maximum_duration 时间内分配完。更详细的参数配置情况,请参见 参考指南(MySQL 模式) 和 参考指南(ORACLE 模式) 。
注意
开启写入限速之后,当 MEMstore 的使用量达到
writing_throttling_trigger_percentage的百分比后,DML 语句的的运行时间会变大,这个是符合技术预期的,如果需要保证 SQL 执行完,有可能是需要将ob_query_timout及ob_trx_timeout设置为一个较大的值。但是用户感知的响应时间是会受到影响的。大多数情况下,通过
alter system set writing_throttling_trigger_percentage=90;开启写入限速即可。对于写入速度和转储速度悬殊的集群,可以酌情调小writing_throttling_trigger_percentage的值,在生产环境上请谨慎修改该参数。如果在生产环境一定需要调整writing_throttling_trigger_percentage,需要在 OceanBase 技术支持的指导下对该参数进行进一步地调整和修改。注意
这两个参数为租户级配置项,如果在 sys 租户下设置的话,需要显示的指定 tenant,否则设置的是 sys 租户的配置项。示例:alter system set writing_throttling_trigger_percentage=90 tenant='oracle';
虽然 OceanBase 有能力对大并发的 DML 负载进行 MemStore 的写入限速,使得大量并发进入到 OBServer 的 DML 语句能够更慢速的操作来保证最终被处理完。但是 OBServer 集群对于同时产生的并发写入始终是有技术上限的,在进行 OceanBase MemStore 做写入限速微调的同时也请同步验证并发 DML 的规模是否合理,如果业务允许请通过降低并发 DML 语句的并发量来保证相应的业务场景完成。