
书接上文《多场景 OceanBase 并发参数调整方案》引发的疑问。
当基于 observer.log 查看 1008 租户(租户名:mysql_ob)队列积压情况时看到如下 2 个值,疑惑的是 unit_min_cpu 和 unit_max_cpu 都等于 8 ,如下 2 个值又是如何计算出来的?
min_worker_cnt:34 //最小工作线程数
max_worker_cnt:150 //最大工作线程数
参考《租户资源管理》[1]。
+-------------+-------------------+---------------+---------+---------+
| tenant_name | unit_server | unit_info | min_cpu | max_cpu |
+-------------+-------------------+---------------+---------+---------+
| mysql_ob | 10.186.64.10:2882 | 1 unit: 8C/6G | 8 | 8 |
| mysql_ob | 10.186.64.61:2882 | 1 unit: 8C/6G | 8 | 8 |
| mysql_ob | 10.186.64.62:2882 | 1 unit: 8C/6G | 8 | 8 |
+-------------+-------------------+---------------+---------+---------+
workers_per_cpu_quota = 10
px_workers_per_cpu_quota = 10
cpu_quota_concurrency = 4
parallel_servers_target = 80
我们将对 OceanBase 企业版解析为反汇编代码,再通过社区版开源的源码[2]进行逻辑对应验证。
查看 OceanBase 社区版代码。
int64_t ObTenant::min_worker_cnt() const
{
ObTenantConfigGuard tenant_config(TENANT_CONF(id_));
return 2 + std::max(1L, static_cast<int64_t>(unit_min_cpu() * (tenant_config.is_valid() ? tenant_config->cpu_quota_concurrency : 4)));
}
OceanBase 企业版反汇编代码(对比查看,确认与社区版的逻辑基本是一致的)。
589b54e: movsd 0x328(%r15) //%xmm0 从 ObTenant 对象偏移 0x328 读取双精度浮点数,存入 %xmm0,可能是线程需求的比例或权重。
589b557: test %rax //%rax 检查配置对象是否有效。
589b55a: je 589b599 //如果无效,跳转到默认值处理逻辑。
589b55c: movsd 0x40858(%rax) //%xmm1 从配置对象偏移 0x40858 读取双精度值,可能是基础线程数或配置参数。
589b564: cvttpd2dq %xmm1 //%xmm1 将浮点数截断为整数。
589b568: cvtdq2pd %xmm1 //%xmm1 转换回浮点数,可能为确保精度。
589b56c: mulsd %xmm1 //%xmm0 将租户因子(%xmm0)与配置值(%xmm1)相乘,得到初步线程数。
589b570: cvttsd2si %xmm0 //%rdx 将乘法结果转换为整数,存入 %rdx。
589b575: test %rdx //%rdx 和 589b578: mov $0x1 //%ecx 确保结果不小于 1。
589b57d: cmovg %rdx //%rcx 如果 %rdx 大于 0,取 %rdx,否则取 1。
589b581: lock decq 0x2f0(%rax) //原子操作,可能减少配置对象的引用计数。
589b589: add $0x2 //%rcx 将结果加 2,可能为确保线程数有冗余。
589b58d: mov %rcx //%rax 将最终结果存入 %rax,准备返回。
再将参数代入公式计算。
min_worker_cnt = 2 + max(1, unit_min_cpu * cpu_quota_concurrency)
= 2 + max(1, 8*4)
= 34
查看 OceanBase 社区版代码。
int64_t ObTenant::max_worker_cnt() const
{
return std::max(tenant_meta_.unit_.config_.memory_size() / 20 / (GCONF.stack_size + (3 << 20) + (512 << 10)),
150L);
}
stack_size + 3MB + 512KBOceanBase 企业版反汇编代码(对比查看,确认与社区版的逻辑基本是一致的)。
a815cc6: 48 3d 95 00 00 00 cmp $0x95,%raxa815ccc: b9 96 00 00 00 mov $0x96,%ecx
a815cd1: 48 0f 4e c1 cmovle %rcx,%rax
将 %rax(动态计算的 max_worker_cnt 值) 与 0x95(149) 进行比较,若 %rax ≤ 0x95(149),则将 %rax 设为 0x96 (150)。
确认 stack_size 的值。
+-------+----------+--------------+----------+------------+-----------+-------+-------------------------------------------------------+----------+---------+---------+------------------+
| zone | svr_type | svr_ip | svr_port | name | data_type | value | info | section | scope | source | edit_level |
+-------+----------+--------------+----------+------------+-----------+-------+-------------------------------------------------------+----------+---------+---------+------------------+
| zone1 | observer | 10.186.64.61 | 2882 | stack_size | NULL | 512K | the size of routine execution stackRange: [512K, 20M] | OBSERVER | CLUSTER | DEFAULT | STATIC_EFFECTIVE |
| zone3 | observer | 10.186.64.62 | 2882 | stack_size | NULL | 512K | the size of routine execution stackRange: [512K, 20M] | OBSERVER | CLUSTER | DEFAULT | STATIC_EFFECTIVE |
| zone2 | observer | 10.186.64.10 | 2882 | stack_size | NULL | 512K | the size of routine execution stackRange: [512K, 20M] | OBSERVER | CLUSTER | DEFAULT | STATIC_EFFECTIVE |
+-------+----------+--------------+----------+------------+-----------+-------+-------------------------------------------------------+----------+---------+---------+------------------+
3 rows in set (0.027 sec)
最后将参数代入公式计算(以 MB 为单位计算)。
max_worker_cnt = max(memory_size / 20 / (stack_size + 3MB + 512KB), 150)
= max(6*1024/20/4, 150)
= 150
根据源码计算公式:当 unit_min_cpu * cpu_quota_concurrency > 1 时,将此值 + 2,否则等于 3。
我们将 unit_min_cpu 和 cpu_quota_concurrency 均调整为 1 ,预期 min_worker_cnt 等于 3。
租户 mysql_ob 对执行参数调整并确认生效。
obclient [oceanbase]> alter systemset cpu_quota_concurrency = 1;
Query OK, 0 rows affected (0.698 sec)
obclient [oceanbase]> showparameterslike'cpu_quota_concurrency';
+-------+----------+--------------+----------+-----------------------+-----------+-------+--------------------------------------------------------+---------+--------+---------+-------------------+
| zone | svr_type | svr_ip | svr_port | name | data_type | value | info | section | scope | source | edit_level |
+-------+----------+--------------+----------+-----------------------+-----------+-------+--------------------------------------------------------+---------+--------+---------+-------------------+
| zone1 | observer | 10.186.64.61 | 2882 | cpu_quota_concurrency | NULL | 1 | max allowed concurrency for 1 CPU quota. Range: [1,20] | TENANT | TENANT | DEFAULT | DYNAMIC_EFFECTIVE |
| zone3 | observer | 10.186.64.62 | 2882 | cpu_quota_concurrency | NULL | 1 | max allowed concurrency for 1 CPU quota. Range: [1,20] | TENANT | TENANT | DEFAULT | DYNAMIC_EFFECTIVE |
| zone2 | observer | 10.186.64.10 | 2882 | cpu_quota_concurrency | NULL | 1 | max allowed concurrency for 1 CPU quota. Range: [1,20] | TENANT | TENANT | DEFAULT | DYNAMIC_EFFECTIVE |
+-------+----------+--------------+----------+-----------------------+-----------+-------+--------------------------------------------------------+---------+--------+---------+-------------------+
3 rows in set (0.015 sec)
obclient [oceanbase]> ALTERRESOURCE unit config_mysql_ob_zone1_u1c6g_qfu MIN_CPU 1, MAX_CPU 8;
Query OK, 0 rows affected (0.019 sec)
obclient [oceanbase]> ALTERRESOURCE unit config_mysql_ob_zone3_u1c6g_uyr MIN_CPU 1, MAX_CPU 8;
Query OK, 0 rows affected (0.031 sec)
obclient [oceanbase]> ALTERRESOURCE unit config_mysql_ob_zone2_u1c6g_lli MIN_CPU 1, MAX_CPU 8;
Query OK, 0 rows affected (0.014 sec)
obclient [oceanbase]> SELECT a.tenant_name, a.tenant_id, b.name unit_config, c.name pool_name, b.min_cpu, b.max_cpu, MEMORY_SIZE/1024/1024/1024as MEMORY_SIZE
-> FROM OCEANBASE.DBA_OB_TENANTS a, OCEANBASE.DBA_OB_UNIT_CONFIGS b, OCEANBASE.DBA_OB_RESOURCE_POOLS c
-> WHERE a.tenant_id = c.tenant_id
-> AND b.unit_config_id = c.unit_config_id
-> AND a.tenant_name = 'mysql_ob'
-> ORDERBY a.tenant_id DESC;
+-------------+-----------+---------------------------------+-------------------------+---------+---------+----------------+
| tenant_name | tenant_id | unit_config | pool_name | min_cpu | max_cpu | MEMORY_SIZE |
+-------------+-----------+---------------------------------+-------------------------+---------+---------+----------------+
| mysql_ob | 1008 | config_mysql_ob_zone1_u1c6g_qfu | pool_mysql_ob_zone1_rhp | 1 | 8 | 6.000000000000 |
| mysql_ob | 1008 | config_mysql_ob_zone3_u1c6g_uyr | pool_mysql_ob_zone3_brf | 1 | 8 | 6.000000000000 |
| mysql_ob | 1008 | config_mysql_ob_zone2_u1c6g_lli | pool_mysql_ob_zone2_aii | 1 | 8 | 6.000000000000 |
+-------------+-----------+---------------------------------+-------------------------+---------+---------+----------------+
3 rows in set (0.041 sec)
可以看到 observer.log 中 min_worker_cnt 的值已经变成 3 了,符合预期。
[root@10-186-64-61 log]$ grep "dump tenant info" observer.log | grep "tenant_id:1008" | tail -1 | grep min_worker_cnt | awk -F'min_worker_cnt:' '{print "min_worker_cnt:" $2}' | awk -F',' '{print $1}'
min_worker_cnt:3
公式:租户内存大小除以 20 再除以单线程内存开销(stack_size(512KB)+ 3MB + 512KB)与 150 进行比较,取较大者。
代入公式,统一以 MB 为单位进行计算,推算租户的 memory_size 大于多少 GB 时,max_worker_cnt 超出 150 。
20*4*150/1024=11.7GBmax_worker_cnt 取 memory_size / 20 / (stack_size + 3MB + 512KB) 的结果值。max_worker_cnt = 153。obclient [oceanbase]> ALTER RESOURCE unit config_mysql_ob_zone1_u8c12g_jzi MEMORY_SIZE '12G';
Query OK, 0 rows affected (0.027 sec)
obclient [oceanbase]> ALTERRESOURCE unit config_mysql_ob_zone2_u8c13g_prk MEMORY_SIZE '12G';
Query OK, 0 rows affected (0.018 sec)
obclient [oceanbase]> ALTERRESOURCE unit config_mysql_ob_zone3_u8c12g_vke MEMORY_SIZE '12G';
Query OK, 0 rows affected (0.022 sec)
obclient [oceanbase]> SELECT a.tenant_name, a.tenant_id, b.name unit_config, c.name pool_name, b.min_cpu, b.max_cpu, MEMORY_SIZE/1024/1024/1024as MEMORY_SIZE
-> FROM OCEANBASE.DBA_OB_TENANTS a, OCEANBASE.DBA_OB_UNIT_CONFIGS b, OCEANBASE.DBA_OB_RESOURCE_POOLS c
-> WHERE a.tenant_id = c.tenant_id
-> AND b.unit_config_id = c.unit_config_id
-> AND a.tenant_name = 'mysql_ob'
-> ORDERBY a.tenant_id DESC;
+-------------+-----------+----------------------------------+-------------------------+---------+---------+-----------------+
| tenant_name | tenant_id | unit_config | pool_name | min_cpu | max_cpu | MEMORY_SIZE |
+-------------+-----------+----------------------------------+-------------------------+---------+---------+-----------------+
| mysql_ob | 1008 | config_mysql_ob_zone1_u8c12g_jzi | pool_mysql_ob_zone1_rhp | 8 | 8 | 12.000000000000 |
| mysql_ob | 1008 | config_mysql_ob_zone2_u8c13g_prk | pool_mysql_ob_zone2_aii | 8 | 8 | 12.000000000000 |
| mysql_ob | 1008 | config_mysql_ob_zone3_u8c12g_vke | pool_mysql_ob_zone3_vke | 8 | 8 | 12.000000000000 |
+-------------+-----------+----------------------------------+-------------------------+---------+---------+-----------------+
3 rows in set (0.065 sec)
可以看到 observer.log 中 max_worker_cnt 的值为 150,而不是 153,不符合预期。
[root@10-186-64-61 log]$ grep "dump tenant info" observer.log | grep "tenant_id:1008" | tail -1 | grep max_worker_cnt | awk -F'max_worker_cnt:' '{print "max_worker_cnt:" $2}' | awk -F',' '{print $1}'
max_worker_cnt:150
查看 observer.log 中的 memory_size 大小。
[root@10-186-64-61 log]$ grep "dump tenant info" observer.log | grep "tenant_id:1008" | tail -1 | grep memory_size | awk -F'memory_size:' '{print "memory_size:" $2}' | awk -F',' '{print $1}'
memory_size:"10.8GB"
可以看到 memory_size 是 10.8GB ,而不是 12GB,原因是什么呢?
将租户内存调整为 14GB。
max_worker_cnt = 14 * 0.9 * 1024 / 20 / 4
=161
obclient [oceanbase]> ALTER RESOURCE unit config_mysql_ob_zone1_u8c12g_jzi MEMORY_SIZE '14G';
Query OK, 0 rows affected (0.027 sec)
obclient [oceanbase]> ALTERRESOURCE unit config_mysql_ob_zone2_u8c13g_prk MEMORY_SIZE '14G';
Query OK, 0 rows affected (0.018 sec)
obclient [oceanbase]> ALTERRESOURCE unit config_mysql_ob_zone3_u8c12g_vke MEMORY_SIZE '14G';
Query OK, 0 rows affected (0.022 sec)
obclient [oceanbase]> SELECT a.tenant_name, a.tenant_id, b.name unit_config, c.name pool_name, b.min_cpu, b.max_cpu, MEMORY_SIZE/1024/1024/1024as MEMORY_SIZE
-> FROM OCEANBASE.DBA_OB_TENANTS a, OCEANBASE.DBA_OB_UNIT_CONFIGS b, OCEANBASE.DBA_OB_RESOURCE_POOLS c
-> WHERE a.tenant_id = c.tenant_id
-> AND b.unit_config_id = c.unit_config_id
-> AND a.tenant_name = 'mysql_ob'
-> ORDERBY a.tenant_id DESC;
+-------------+-----------+----------------------------------+-------------------------+---------+---------+-----------------+
| tenant_name | tenant_id | unit_config | pool_name | min_cpu | max_cpu | MEMORY_SIZE |
+-------------+-----------+----------------------------------+-------------------------+---------+---------+-----------------+
| mysql_ob | 1008 | config_mysql_ob_zone1_u8c12g_jzi | pool_mysql_ob_zone1_rhp | 8 | 8 | 14.000000000000 |
| mysql_ob | 1008 | config_mysql_ob_zone2_u8c13g_prk | pool_mysql_ob_zone2_aii | 8 | 8 | 14.000000000000 |
| mysql_ob | 1008 | config_mysql_ob_zone3_u8c12g_vke | pool_mysql_ob_zone3_vke | 8 | 8 | 14.000000000000 |
+-------------+-----------+----------------------------------+-------------------------+---------+---------+-----------------+
3 rows in set (0.065 sec)
注意:基于 OCP 新建了租户规格并进行调整,故
unit_config名称发生变化。
可以看到 observer.log 中 min_worker_cnt 的值已经变成 161 了,符合预期。
[root@10-186-64-61 log]$ grep "dump tenant info" observer.log | grep "tenant_id:1008" | tail -1 | grep max_worker_cnt | awk -F'max_worker_cnt:' '{print "max_worker_cnt:" $2}' | awk -F',' '{print $1}'
max_worker_cnt:161
OceanBase 数据库 V4 版本对 min_worker_cnt (最小工作线程数)和 max_worker_cnt (最大工作线程数)的下限作了限制,前者最小值是 3 ,后者最小值是 150, 以保障租户有足够的资源正常运行。
参考资料
[1]
租户资源管理: https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000002013998
[2]
OceanBase 社区版源码: https://github.com/oceanbase/oceanbase/tree/develop
本文关键字:#OceanBase #性能参数