当前位置: 首页 > news >正文

第二章 OB 存储引擎高级技术

第二章主要讲 OceanBase 内存分布、存储原理、合并和转储管理。

2.1 内存管理

OceanBase会占据物理服务器的大部分内存并进行统一管理。

2.1.1 OBServer 整体内存分配

通过参数设定 OBServer 占用的内存上限

  • memory_limit
  • memory_limit_percentage

每一个 OBServer 都包含多个租户(sys租户和业务租户)的数据,但 OBServer 的内存并不是全部分配给租户。 OBServer 中有些内存不属于任何租户,属于所有租户共享的资源,称为“系统内部内存”。

系统内部内存可使用的内存上限是通过 system_memory 系统参数配置的,它的含义是系统内部可使用 OceanBase 内存上限。例如,假设OceanBase 内存上限为80 GB, system_memory 的值为8G,可用于租户分配的内存就是剩下80-8=72 GB。

OBServer 整体内存分配示意图

2.1.2 租户内部内存分配

租户内部内存分配示意图

每个租户内部的内存分模块划分,需要关注的主要是包括

  • 不可动态伸缩的内存:MemStore
  • 可动态伸缩的内存:KVCache
  • 执行计划缓存:PLAN CACHE
  • 执行 SQL 内存:SQL AREA,主要是 parser 和优化器 使用
  • 工作线程内存:WORKER AREA

MemStore用来保存DML产生的增量数据 ,空间不可被占用;KVCache空间会被其它众多内存模块复用。

除去 MemStore 和 KVCache ,其他模块的内存使用大小默认不超过10G。

查看内存使用大于 10G 的其它模块,SQL 样例:

select * from gv$memory whereused > 1024*1024*1024*10and `context` not in ('OB_MEMSTORE','OB_KVSTORE_CACHE');

查看内存占用最大的 10 个模块,SQL 样例

select`CONTEXT`,ROUND(USED / 1024 / 1024 / 1024, 2) as USED_GB
from gv$memory
group by `CONTEXT`
ORDER BY USED DESC 
limit 10;

2.1.3 常见内存问题处理

2.1.3.1 ERROR 4030 (HY000): OB4030: Over tenant memory limits

遇到 Over tenant memory limits 错误时,首先需判断是不是MemStore 内存超限,当MmeStore 内存超限时,需要检查数据写入是否过量或未做限流。当遇到大量写入且数据转储跟不上写入速度的时候就会报这种错误。运行下述语句查看内存状态。

select  /*+ READ_CONSISTENCY(WEAK), query_timeout (100000000) */TENANT_ID,round(ACTIVE / 1024 / 1024 / 1024, 2) ACTIVE_GB,round(TOTAL / 1024 / 1024 / 1024, 2) TOTAL_GB,round(FREEZE_TRIGGER / 1024 / 1024 / 1024, 2) FREEZE_TRIGGER_GB,round(TOTAL / FREEZE_TRIGGER * 100, 2) percent_trigger,round(MEM_LIMIT / 1024 / 1024 / 1024, 2) MEM_LIMIT_GB
fromgv$memstore
wheretenant_id > 1000or TENANT_ID = 1
order bytenant_id,TOTAL_GB desc;

该问题的紧急应对措施是增加租户内存。问题解决之后需要分析原因,如果是因为未做限流引起,需要加上相应措施,然后回滚之前加上的租户内存动作。如果确实因为业务规模增长导致租户内存不足以支撑业务时,需要根据转储的频度设置合理的租户内存大小。

如 MemStore 内存未超限,判断是 MemStore 之外的哪个 module 占用内存空间最高

select  tenant_id,  svr_ip,  mod_name,  sum(hold) module_sum
from  __all_virtual_memory_info
where  tenant_id > 1000  and hold <> 0and mod_name not in ('OB_KVSTORE_CACHE', 'OB_MEMSTORE')
group by tenant_id,  svr_ip,  mod_name
order by  module_sum desc;

模块内存超限,可能需要先调整单独模块的内存,如

  • ob_sql_work_area_percentage (排序、分布式中间结果)
  • ob_interm_result_mem_limit (分布式中间结果)

如租户内存过小,也需要加租户内存。

2.1.3.2 500 租户内存超限

tenant_id =500 的租户是 OB 内部租户,简称 500 租户。

500 租户的内存使用量没有被 v$memory 和 gv$memory 统计,需要查询 __all_virtual_memory_info 表。

select * from __all_virtual_memory_info where tenant_id=500;
2.1.3.3 OBServer 错误日志 allocmemory 或 allocate memory

报错的原因,通常是系统内存耗尽,或者已经达到了内存使用的上限。

日志样例

…… alloc memory failed ( 4013, size=65536, mem=NULL, label_="OB_SQL_HASH_SET", ctx_id _=
…… oops, alloc failed , tenant_id =1001 ctx_id =9 hold=266338304 limit=268435455
…… allocate memory failed nbyte =213552, operator type="PHY_MULTI_PART_INSERT")

处理方式

  • 模块内存超限,先调整单独模块的内存
  • 租户内存过小,加租户内存

2.2 存储管理

2.2.1 LSM Tree

用一句话来简单描述的话, LSM Tree 就是一个基于归并排序的数据存储思想。

LSM Tree(The Log-Structured Merge-Tree)

  • 将某个对象(Partition)中的数据按照 “key-value” 形式在磁盘上有序存储(SSTable);
  • 数据更新先记录在MemStore中的MemTable里,然后再合并(Merge)到底层的 ssTable 里;
  • SSTable 和 MemTable 之间可以有多级中间数据,同样以key-value 形式保存在磁盘上,逐级向下合并。

SSTable

  • SSTable将数据按照主键或者隐含列(不可见)的顺序在磁盘上有序排列,以B+ Tree数据结构实现“key-value”存储。
  • 数据被分成2MB的固定大小宏块(Macro Block),每个宏块包含一定key值范围的数据。
  • 为了避免读取少量数据时的“读放大”,每个宏块内部又分为多个微块(Micro Block),大小一般配为16KB(可变);微块内的记录数和具体的数据特征相关。

2.2.2 合并

OceanBase 中最简单的 LSM Tree只有C0层(MemTable)和C1层(SSTable)。两层数据的合并过程如下:

  • 将所有 OBServer 上的MemTable 数据做大版本冻结(Major Freeze),其余内存作为新的 MemTable 继续使用;
  • 将冻结后的 MemTable 数据合并(Merge)到 SSTable 中,形成新的 SSTable,并覆盖旧的 SSTable;
  • 合并完成后,冻结的MemTable内存才可以被清空并重新使用。

合并示意图

合并操作(Major Freeze)是将动静态数据做归并,也就是产生新的C1层的数据,会比较费时。当转储产生的增量数据积累到一定程度时,通过 Major Freeze 实现大版本的合并。由于在合并的过程中为了保证数据的一致性,就需

要在合并的过程中暂停正在被合并的数据上的事务,这对性能来说是会有影响的,OceanBase 对合并操作进行了细化,分为增量合并,轮转合并和全量合并。

2.3.3 转储

为什么有转储?原因是合并存在一系列问题

  • 资源消耗高,对在线业务性能影响较大。
  • 单个租户 MemStore 使用率高会触发集群级合并,其它租户成为受害者。
  • 合并耗时长,MemStore内存释放不及时,容易造成MemStore满而数据写入失败的情况。

为了解决 2 层 LSM Tree 合并时引发的问题(资源消耗大,内存释放速度慢等),引入了“转储”机制:

  • 将 MemTable 数据做小版本冻结(Minor Freeze)后写到磁盘上单独的转储文件里,不与SSTable数据做合并;
  • 转储文件写完之后,冻结的 MemTable 内存被清空并重新使用;
  • 每次转储会将 MemTable 数据与前一次转储的数据合并(Merge),转储文件最终会合并到 SSTable 中。

转储的设计思路

  • 每个 MemStore 触发单独的冻结(freeze_trigger_percentage)及数据合并,不影响其它租户;也可以通过命令为指定指定租户、指定OBServer 、指定分区做转储。
  • 只和上一次转储的数据做合并,不和 SSTable 的数据做合并。

转储示意图

可以看到转储引入了一层 Minor SSTable,其原理是把内存中的增量数据会被以 MemTable 的方式保存在磁盘中。

转储的优势

  • 每个租户的转储不影响 OBServer 上其它的租户,也不会触发集群级转储,避免关联影响。•
  • 资源消耗小,对在线业务性能影响较低。
  • 耗时相对较短,MemStore 更快释放,降低发生 MemStore 写满的概率。

转储的副作用

  • 数据层级增多,查询链路变长,查询性能下降。
  • 冗余数据增多,占用更多磁盘空间。

2.2.4 合并管理

三种合并触发方式

  • 定时合并
  • MemStore 使用率达到阈值自动合并
  • 手动合并
2.2.4.1 定时合并(每日合并)

定时合并也叫做每日合并,通过系统参数 major_freeze_duty_time 设置每日合并触发时间。

-- 缺省的每日合并时间为凌晨 2:00-- 查看
show parameters like 'major_freeze_duty_time' \G-- 设置
alter system set major_freeze_duty_time='02:00'

控制每日合并策略相关系统参数

  • enable_manual_merge:是否开启手动合并(默认值False);
  • enable_merge_by_turn:是否开启自动轮转合并(默认值True);
  • zone_merge_order:指定自动轮转合并的合并顺序(默认值NULL);
  • major_freeze_duty_time:每日合并触发时间,有效的格式为 HH:SS, 值为 disable 时,不执行每日合并
2.2.4.2 MemStore 使用率达到阈值合并

当租户的 MemStore 内存使用率达到 freeze_trigger_percentage参数的值, 并且转储的次数已经达到了major_compact_trigger/minor_freeze_times 参数的值,会自动触发合并。

相关 SQL 命令

-- 查看 MemStore
select * from oceanbase.v$memstore;-- 相关系统参数
alter system set freeze_trigger_percentage = 40;
alter system set major_compat_trigger = 100;
alter system set minor_freeze_times = 100;

注意:任意一个租户的 MemStore 写到一定的比例会后会自行发起集群冻结

所谓 MemStore ,是指租户申请的内存资源中有多少可以来存放更新数据, 比如一个租户可使用的内存为 8G (建立租户时,Resource Unit 的 min_memory), 系统参数 memstore_limit_percentage 控制有多少内存可以用来存写入的数据(其余的内存会被用作其它用途,比如缓存)。

2.2.4.3 手动合并

手动发起合并 SQL 命令(本质上是发起冻结)

alter system major freeze;

注意这个命令是发起 冻结,其执行结果是把集群的冻结版本号 +1,合并是 OceanBase 集群对冻结版本变化的自动响应行为。这个命令可以重复调用,调用几次冻结版本号就增加几,不受当前是否正在合并状态的影响。

可以通过 __all_Zone 表查看合并状态

select * from __all_zone where name = 'merge_status';

结果样例, Zone 为空表示集群的合并状态

2.2.4.5 轮转合并

借助自身天然具备的多副本分布式架构,OceanBase引入了轮转合并机制:

  • 一般情况下,OceanBase会有3份(或更多)数据副本;可以轮流为每份副本单独做合并;
  • 当一个副本在合并时,这个副本上的业务流量可以暂时切到其它没有合并的副本上;
  • 某个副本合并完成后,将流量切回这个副本,然后以类似的方式为下一个副本做合并,直至所有副本完成合并。

轮转合并相关系统参数:

  • 通过参数 enable_merge_by_turn 开启或者关闭轮转合并;
  • 通过参数 zone_merge_order 设置合并顺序,设置为空表示取消自定义轮转顺序;
  • 通过参数 zone_merge_concurrency 设置轮转合并并发度,默认为 3,表示最多有 3 个 Zone 可以一起合并。

关于轮转合并的更多说明

  • 以ZONE为单位轮转合并,只有一个ZONE合并完成后才开始下一个ZONE的合并;合并整体时间变长;
  • 某一个ZONE的合并开始之前,会将这个ZONE上的Leader服务切换到其它ZONE;切换动作对长事务有影响;
  • 由于正在合并的ZONE上没有Leader,避免了合并对在线服务带来的性能影响;

轮转合并性能更友好,非轮转合并也有优势,因为非轮转合并是所有Zone一起进行合并,相对于轮转合并:

  • 非轮转合并不做切主操作,而 OceanBase 切主过程会
  • 影响长事务执行,比如可能会因此遇到事务超时
  • 导致表的自增列 ID 增加自增不长(缺省值为 100W)
  • 非轮转合并不受合并并发度的限制
2.2.4.6 其它合并控制系统参数

参数 merge_thread_count 控制合并线程数

  • 控制可以同时执行合并的分区个数;单分区暂不支持拆分合并,分区表可以加快合并速度。
  • 默认值为0,实际取值为min(10,cpu_cnt * 0.3)。
  • 最大取值不要超过48:值太大会占用太多CPU和IO资源,对OBServer 的性能影响较大;而且容易触
  • 发系统报警,比如CPU使用率超过90%可能会触发主机报警。
  • 如对合并速度没有特殊要求,建议使用默认值0 。

参数 max_kept_major_version_number 控制STable中保留的数据合并版本个数

  • 调大参数值可以保留更多历史数据,但同时占用更多的存储空间。
  • 在 hint 中利用 frozen_version(<major_version>)指定历史版本。

参数zone_merge_timeout定义超时阈值

  • 默认值为'3h'(3个小时)。
  • 如果某个ZONE的合并执行超过阈值,合并状态被设置为TIMEOUT。

参数data_disk_usage_limit_percentage定义数据盘空间使用阈值

  • 默认值90。
  • 当数据盘空间使用量超过阈值后,合并任务打印ERROR警告日志,合并任务失败;需要尽快扩大数据盘物理空间,并调大data_disk_usage_limit_percentage参数的值。
  • 当数据盘空间使用量超过阈值后,禁止数据迁入。
2.2.4.7 ✨查看 Zone 合并记录和状态

通过 __all_rootservice_event_history 表,查看合并记录 SQL 样例:

select * from __all_rootservice_event_history where module = 'daily_merge' limit 100 ;

通过 __all_Zone 表查看合并状态。__all_Zone 表是类似 key/value 的设计。行转列查看每个 Zone 合并详情 SQL 样例:

SELECT `zone`,                                 MAX(CASE `name` WHEN 'region' THEN `info` ELSE '' END ) `region`,                           MAX(CASE `name` WHEN 'idc' THEN `info` ELSE '' END ) `idc`,                                 MAX(CASE `name` WHEN 'status' THEN `info` ELSE '' END ) `status`,                           MAX(CASE `name` WHEN 'merge_status' THEN `info` ELSE '' END ) `merge_status`,               MAX(CASE `name` WHEN 'broadcast_version' THEN `value` ELSE 0 END ) `broadcast_version`,     MAX(CASE `name` WHEN 'all_merged_version' THEN `value` ELSE 0 END ) `all_merged_version`,   MAX(CASE `name` WHEN 'last_merged_version' THEN `value` ELSE 0 END ) `last_merged_version`, MAX(CASE `name` WHEN 'merge_start_time' THEN `value` ELSE 0 END ) `merge_start_time`,       MAX(CASE `name` WHEN 'last_merged_time' THEN `value` ELSE 0 END ) `last_merged_time`,       MAX(CASE `name` WHEN 'is_merge_timeout' THEN `value` ELSE 0 END ) `merge_timeout`           FROM oceanbase.__all_zone WHERE `zone` <> '' GROUP BY `zone`;

Zone 相关信息

  • all_merged_version 表示本 Zone 最近一次已经合并完成的版本
  • broadcast_version 表示本 Zone 收到的可以进行合并的版本
  • is_merge_timeout 如果在一段时间还没有合并完成,则将此字段置为 1 ,表示合并时间太长
  • 具体时间可以通过系统参数 zone_merge_timeout 来控制
  • merge_start_time / last_merged_time 分别表示合并开始、结束时间
  • last_merged_version 表示本 Zone 最近一次合并完成的版本
  • merge_status 表示本 Zone 的合并状态,取值有:IDLE/MERGING/TIMEOUT/ERROR
  • suspend_merging 表示是否暂停合并,有时候因为合并造成压力过大或其它原因,我们想暂停合并
  • 通过 alter system suspend merge 来暂停合并
  • 通过 alter system resume merge 来恢复合并

Zone 为空时表示整个集群的状态,查看集群合并详情 SQL 样例

SELECT                                     MAX(CASE `name` WHEN 'cluster' THEN `info` ELSE '' END ) `cluster`,                                   MAX(CASE `name` WHEN 'merge_status' THEN `info` ELSE '' END ) `merge_status`,                         MAX(CASE `name` WHEN 'global_broadcast_version' THEN `value` ELSE 0 END )  `global_broadcast_version`,MAX(CASE `name` WHEN 'frozen_version' THEN `value` ELSE 0 END ) `frozen_version`,                MAX(CASE `name` WHEN 'last_merged_version' THEN  `value` ELSE 0 END ) `last_merged_version`,          MAX(CASE `name` WHEN 'frozen_time' THEN `value` ELSE 0 END ) `frozen_time`,                           MAX(CASE `name` WHEN 'is_merge_error' THEN `value` ELSE 0 END ) `merge_error`                    FROM oceanbase.__all_zone  WHERE `zone` = '';

全局信息:

  • frozen_time 表示冻结时间,在上面的例子里,这个值为0,说明系统还没有发生过冻结。
  • frozen_version 表示版本号,从1开始。
  • global_broadcast_version 表示这个版本已经通过各ObServer进行合并。
  • is_merge_error 表示合并过程中是否出现错误。1表示合并遇到了问题,这种情况一般需要手工处理。
  • last_merged_version 表示上次合并完成的版本。
  • try_frozen_version 表示正在进行哪个版本的冻结,如果 try_frozen_version 和
  • frozne_version 相等,则表示该版本已经完成冻结。
  • merge_list 表示 ZONE 的合并顺序。
2.2.4.9 ✨查看 OBServer 合并状态

通过 __all_virtual_Partition_sstable_image_info 表查看 OBServer 合并状态

SELECT /*+read_consistency(weak) QUERY_TIMEOUT(60000000) */                  zone, svr_ip, svr_port, major_version AS `version`,                   merge_start_time AS start_time, merge_finish_time AS finish_time      FROM oceanbase.__all_virtual_partition_sstable_image_info              ORDER BY zone,svr_ip, svr_port, major_version

一般的, __all_virtual_Partition_sstable_image_info 表会保存2个冻结版本号的统计信息,如果 start time 不为 null, 而 end_time 列为 null,表示 OBServer 的合并尚未完成。

2.2.6 转储管理

2.2.6.1 转储相关系统参数

major_compact_trigger /minor_freeze_times

• 控制两次合并之间的转储次数,达到此次数则自动触发合并(Major Freeze)。

• 设置为0表示关闭转储,即每次租户MemStore使用率达到冻结阈值(freeze_trigger_percentage)

都直接触发集群合并。

minor_merge_concurrency

• 并发做转储的分区个数;单个分区暂时不支持拆分转储,分区表可加快速度。

• 并发转储的分区过少,会影响转储的性能和效果(比如MemStore内存释放不够快)。

• 并发转储的分区过多,同样会消耗过多资源,影响在线交易的性能。

2.2.6.2 转储适用场景

转储功能比较适用于以下场景

  • 批处理、大量数据导入等场景,写MemStore的速度很快,需要MemStore内存尽快释放。
  • 业务峰值交易量大,写入MemStore的数据很多,但又不想在峰值时段触发合并(Major Freeze),
  • 希望能将合并延后。

转储场景的常用配置方法

  • 减小 freeze_trigger_percentage的值(比如40)
  • 使MemStore尽早释放,进一步降低MemStore 写满的概率。
  • 增大major_compact_trigger /minor_freeze_times的值
  • 尽量避免峰值交易时段触发合并(Major Freeze),将合并的时机延后到交易低谷时段的每日合并(major_freeze_duty_time)。
2.2.6.3 手动触发转储

手动触发转储命令语法

ALTER SYSTEM MINOR FREEZE
[{TENANT[=] (‘tt1' [, 'tt2'...]) | PARTITION_ID [=] 'partidx%partcount@tableid‘}]
[SERVER [=] ('ip:port' [, 'ip:port'...])];

几个可选的控制参数

  • tenant : 指定要执行minor freeze的租户
  • Partition_id : 指定要执行minor freeze的Partition
  • Server : 指定要执行minor freeze的OBServer

当什么选项都不指定时,默认对所有OBServer 上的所有租户执行转储。

手动触发的转储次数不受参数minor_freeze_times的限制,即手动触发的转储次数即使超过设置的

次数,也不会触发合并(Major Freeze)。

2.2.6.4 ✨查看转储记录和状态

通过 __all_rootservice_event_history 查看转储记录

SELECT  * from __all_rootservice_event_historywhere event = 'root_minor_freeze' limit 10;

通过 gv$memstore 查看上一次合并之后到现在为止租户转储次数

-- 注意这里排除了 500 租户,500 租户的 MEM_LIMIT 是无限制的
selecttenant_id,concat(ip, port) as server,freeze_cnt as freeze_count,round(ACTIVE/1024/1024/1024, 2) as ACTIVE_GB,round(TOTAL/1024/1024/1024, 2) as TOTAL_GB,round(MEM_LIMIT/1024/1024/1024, 2) as LIMIT_GB
from  gv$memstore
where tenant_id <> 500
order by freeze_count desc;

2.2.7 合并与转储对比


文章转载自:
http://bedmate.zzgtdz.cn
http://acceptability.zzgtdz.cn
http://aweigh.zzgtdz.cn
http://ccpit.zzgtdz.cn
http://basophobia.zzgtdz.cn
http://carphology.zzgtdz.cn
http://carburize.zzgtdz.cn
http://cementite.zzgtdz.cn
http://arrivederci.zzgtdz.cn
http://cantor.zzgtdz.cn
http://ceraunograph.zzgtdz.cn
http://alkyne.zzgtdz.cn
http://carageen.zzgtdz.cn
http://adlib.zzgtdz.cn
http://beslaver.zzgtdz.cn
http://cattleya.zzgtdz.cn
http://allocable.zzgtdz.cn
http://brasflia.zzgtdz.cn
http://aimless.zzgtdz.cn
http://chordate.zzgtdz.cn
http://buenaventura.zzgtdz.cn
http://capucine.zzgtdz.cn
http://chickabiddy.zzgtdz.cn
http://athenai.zzgtdz.cn
http://canephora.zzgtdz.cn
http://bypath.zzgtdz.cn
http://adjoining.zzgtdz.cn
http://aborally.zzgtdz.cn
http://aerobus.zzgtdz.cn
http://asemia.zzgtdz.cn
http://www.dtcms.com/a/281739.html

相关文章:

  • 数学金融与金融工程:学科差异与选择指南
  • 【AI News | 20250714】每日AI进展
  • 为 Git branch 命令添加描述功能
  • 将 Vue 3 + Vite + TS 项目打包为 .exe 文件
  • 711SJBH构建制造业信息化人才培训体系的对策-开题报告
  • 21-C#的委托简单使用-1
  • Datawhale 25年7月组队学习coze-ai-assistant Task1学习笔记:动手实践第一个AI Agent—英伦生活口语陪练精灵
  • yolov5、yolov8、yolov11、yolov12如何训练及轻量化部署-netron-onnx
  • echarts折线图的 线条的样式怎么控制
  • Python os模块完全指南:从入门到实战
  • python编程实现GUI界面的排序与查找算法动态模拟演示程序
  • Sa-Token使用要点
  • mongoDB安装初始化及简单介绍
  • 2025/7/15——java学习总结
  • Pandas 和 NumPy 使用文档整理
  • 大宗现货电子盘交易系统核心功能代码解析
  • QT6 源,六章事件系统(8)QEvent 的孙子类:QEnterEvent 光标进入
  • 无穿戴动捕如何凭借摄像头视觉识别算法,赋能高校专业教学革新?
  • python进阶
  • 145-变分模态分解VMD与平稳小波变换SWT信号降噪算法实现!
  • 4G模块 A7680通过MQTT协议连接到onenet(新版)
  • WebAPIs基本认知,DOM基础介绍
  • 基于Springboot+UniApp+Ai实现模拟面试小工具五:权限校验参数校验及日志功能实现
  • Ubuntu 22.04 安装 mysql-server与 postgreSQL 服务端
  • el-tooltip 快速滚动的时候出现残影如何解决 vue3
  • 30.安卓逆向2-frida hook技术-hook So文件(获取app加载的所有模块、导入函数、导出函数、hook SO函数)
  • 区块链发展史全景长图
  • 强化学习Reinforcement Learning
  • 基于Python的图像文字识别系统
  • Glide双内存缓存加载机制详细介绍