OceanBase 压测时为什么冻结阈值在变化?

2023年 7月 22日 40.6k 0

本文从源码角度分析了 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)。
  • 获取资源管理器成功后,最终冻结阈值 memstore_freeze_trigger 的计算基于以下两个值中的一个:
    • mem_memstore_limit:由参数 memstore_limit_percentage 设置。
    • max_mem_memstore_can_get_now:根据实时内存情况计算得出,即 max_mem_memstore_can_get_now = 租户下当前未分配的内存 + memstore持有内存 + kv_cache 持有内存。
  • 取上面两个值中较小的一个记为min,根据min 的大小,以100为分界线分为两种计算方法(此处分两种计算意义不大,已提PR):
    • 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/

    相关文章

    Oracle如何使用授予和撤销权限的语法和示例
    Awesome Project: 探索 MatrixOrigin 云原生分布式数据库
    下载丨66页PDF,云和恩墨技术通讯(2024年7月刊)
    社区版oceanbase安装
    Oracle 导出CSV工具-sqluldr2
    ETL数据集成丨快速将MySQL数据迁移至Doris数据库

    发布评论