本文从源码角度分析了 OceanBase 压测中冻结阈值动态变化的原因,并给出运维建议。
作者:张乾
外星人2号,兼任五位喵星人的铲屎官。
本文来源:原创投稿
- 爱可生开源社区出品,原创内容未经授权不得随意使用,转载请联系小编并注明来源。
背景
测试在用小租户进行压测时,注意到监控中触发内存冻结的阈值是动态变化的,现象如下:
- 非压测时: 冻结阈值表现为 OceanBase 官方文档说明的
memstore_limit_percentage * freeze_trigger_percentage
所计算的值,约为 322MB。 - 压测时: 冻结阈值表现出不规律的降低,但没有出现升高的情况,即始终低于非压测时的 322MB。
为什么压测时该阈值会变化?接下来分析一下现象的原因。
环境信息
版本信息 | OceanBase_CE 4.1.0.0 |
租户规格 | unit 8C4G |
memstore_limit_percentage | 50(默认值) |
freeze_trigger_percentage | 20(默认值) |
sysbench 命令
sysbench /usr/share/sysbench/oltp_read_only.lua --mysql-host=x.x.x.x --mysql-port=42881 --mysql-db=sysbenchdb --mysql-user="sysbench@sysbench_tenant2" --mysql-password=sysbench --tables=10 --table_size=1000000 --threads=1024 --time=120 --report-interval=10 --db-driver=mysql --db-ps-mode=disable --skip-trx=on --mysql-ignore-errors=6002,6004,4012,2013,4016,1062 run
排查过程
确认监控采集的正确性
根据监控指标,在官方文档中找到采集 SQL 如下:
select /* MONITOR_AGENT */ con_id tenant_id, stat_id, value from v$sysstat, DBA_OB_TENANTS where stat_id IN (130002) and (con_id > 1000 or con_id = 1) and class get_cache_hold();
// 如果租户总内存持有值大于租户内存上限,说明溢出
if (tenant_mem_limit < tenant_mem_hold) {
LOG_WARN("[TenantFreezer] tenant_mem_limit is smaller than tenant_mem_hold",
K(tenant_mem_limit), K(tenant_mem_hold), K(tenant_id));
}
// is_add_overflow接收三个参数,第一个和第二个参数相加的和如果小于0,返回 true,表明溢出
// 否则,将前两个参数的和赋值给第三个参数,然后返回flase,表明未溢出
// 此处max_mem_memstore_can_get_now = (tenant_mem_limit - tenant_mem_hold)+ tenant_memstore_hold
// 即max_mem_memstore_can_get_now = 租户当前未分配的内存 + 当前memstore已持有的值
else if (is_add_overflow(tenant_mem_limit - tenant_mem_hold,
tenant_memstore_hold,
max_mem_memstore_can_get_now)) {
if (REACH_TIME_INTERVAL(1 * 1000 * 1000)) {
LOG_WARN("[TenantFreezer] max memstore can get is overflow", K(tenant_mem_limit),
K(tenant_mem_hold), K(tenant_memstore_hold), K(tenant_id));
}
}
// 此处二次调用is_add_overflow,即max_mem_memstore_can_get_now = max_mem_memstore_can_get_now + kv_cache_mem
// 代入上一步的计算式,即max_mem_memstore_can_get_now = (tenant_mem_limit - tenant_mem_hold)+ tenant_memstore_hold + kv_cache_mem
// 总结:max_mem_memstore_can_get_now = 租户下当前未分配的内存 + memstore 持有内存 + kv_cache 持有内存
else if (is_add_overflow(max_mem_memstore_can_get_now,
kv_cache_mem,
max_mem_memstore_can_get_now)) {
if (REACH_TIME_INTERVAL(1 * 1000 * 1000)) {
LOG_WARN("[TenantFreezer] max memstore can get is overflow",
K(tenant_mem_limit), K(tenant_mem_hold), K(tenant_memstore_hold),
K(kv_cache_mem), K(tenant_id));
}
} else {
// 以上都没溢出的话,is_overflow 设置为 false
is_overflow = false;
}
int64_t min = mem_memstore_limit;
if (!is_overflow) {
// 如果未溢出,取 mem_memstore_limit 和max_mem_memstore_can_get_now 中较小的值来计算最终的冻结触发阈值
min = MIN(mem_memstore_limit, max_mem_memstore_can_get_now);
}
// 各内存单位为字节,这里以 100 为分界线分为两种计算方式,意义不大,已提 pr
if (min < 100) {
memstore_freeze_trigger = get_freeze_trigger_percentage_() * min / 100;
} else {
memstore_freeze_trigger = min / 100 * get_freeze_trigger_percentage_();
}
}
// result
ctx.max_mem_memstore_can_get_now_ = max_mem_memstore_can_get_now;
ctx.memstore_freeze_trigger_ = memstore_freeze_trigger;
ctx.kvcache_mem_ = kv_cache_mem;
return ret;
}
计算方式小结
ObTenantFreezer::get_freeze_trigger_()
方法中涉及七个内存相关的变量:
- tenant_mem_limit:租户内存上限
- tenant_mem_hold:租户内存持有值
- mem_memstore_limit:租户 memstore 上限
- tenant_memstore_hold:租户 memstore 持有值
- kv_cache_mem:kv_cache 持有值
- max_mem_memstore_can_get_now:memstore 当前能达到的最大值
- memstore_freeze_trigger:触发冻结的阈值
freeze_trigger_percentage / 100 * memstore_limit
进行计算(此处存在缺陷,计算结果恒为 0,已提 PR)。- mem_memstore_limit:由参数 memstore_limit_percentage 设置。
- max_mem_memstore_can_get_now:根据实时内存情况计算得出,即 max_mem_memstore_can_get_now = 租户下当前未分配的内存 + memstore持有内存 + kv_cache 持有内存。
- min<100:get_freeze_trigger_percentage_() * min / 100
- min ≥ 100:min / 100 * get_freeze_trigger_percentage_()
综上,当 max_mem_memstore_can_get_now < mem_memstore_limit
时,监控图上的冻结阈值就会有变化,且这个冻结阈值不可能高于由 mem_memstore_limit
计算得出的阈值,这与我们观察到的冻结阈值曲线一致。
此外,对于在理解源码时发现的两处代码逻辑缺陷,已提 PR 和 Issue。
总结
在 OceanBase 中,冻结阈值并不是一个固定的值,它会根据当前内存情况进行实时判断。当存在除了 MemStore 和 kv_cache 之外的内存模块占用了较多的内存,挤占了 MemStore 的内存上限时,冻结阈值就会相应地降低。
结合本文场景监控图,租户 fuse_row_cache **(蓝线)**与冻结阈值曲线的变化趋势正好相反,说明该场景下应该是 fuse_row_cache 的增长挤占了 memstore 的内存,导致冻结阈值变低。
运维建议
当我们的运维同学发现冻结阈值发生变化时,说明存在其他内存模块挤占了 memstore 的内存,此时需要格外注意内存的使用情况,结合实际业务场景评估影响面。
另外,需要注意的是,max_mem_memstore_can_get_now
计算式中的 kv_cache 并不是所有可动态伸缩内存(比如 fuse_row_cache/user_row_cache
等)加起来的和,而是特指内存模块 KvstorCacheMb ,这部分将留到下一篇文章介绍。
更多技术文章,请访问:opensource.actionsky.com/