作者: yangzhj 原文来源:https://tidb.net/blog/9418fa0e
1. 背景
从 TiDB 7.1 LTS 版本开始,TiDB 提供了 使用资源管控 (Resource Control) 实现资源隔离 功能, 并且提供了两种方式估算集群的资源总 RU 量,分别是:
以下是基于硬件部署估算容量的说明:
这种方式主要根据当前的集群配置,结合对不同负载观测的经验值进行预估。由于不同类型的负载对硬件的配比要求不同,相同配置的硬件所输出的容量也会有所不同。这里的 WORKLOAD 参数提供了以下不同的负载类型供选择,默认为 TPCC:
TPCC:数据写入较重的负载,根据类似 TPC-C 的负载模型预测。
OLTP_WRITE_ONLY:数据写入较重的负载,根据类似 sysbench oltp_write_only 的负载模型预测。
OLTP_READ_WRITE:数据读写平衡的负载,根据类似 sysbench oltp_read_write 的负载模型预测。
OLTP_READ_ONLY:数据读取较重的负载,根据类似 sysbench oltp_read_only 的负载模型预测。
TPCH_10:AP 类型查询,根据 TPCH-10G 的 22 条查询进行负载预测。
下面我们将通过阅读代码来探索基于硬件部署估算容量(TPCC 负载)的实现逻辑。
2. 探索过程
说明:以下代码基于 TiDB 8.1.1,示例集群为一套 2C 配置的 1pd 1kv 1tidb 架构的单主机集群。
首先通过搜索代码找到相关函数:staticCalibrate,函数具体定义如下:
func (e *Executor) staticCalibrate(req *chunk.Chunk) error {
resourceGroupCtl := domain.GetDomain(e.Ctx()).ResourceGroupsController()
// first fetch the ru settings config.
if resourceGroupCtl == nil {
return errors.New("resource group controller is not initialized")
}
clusterInfo, err := infoschema.GetClusterServerInfo(e.Ctx())
if err != nil {
return err
}
ruCfg := resourceGroupCtl.GetConfig()
if e.WorkloadType == ast.TPCH10 {
return staticCalibrateTpch10(req, clusterInfo, ruCfg)
}
totalKVCPUQuota, err := getTiKVTotalCPUQuota(clusterInfo)
if err != nil {
return errNoCPUQuotaMetrics.FastGenByArgs(err.Error())
}
totalTiDBCPUQuota, err := getTiDBTotalCPUQuota(clusterInfo)
if err != nil {
return errNoCPUQuotaMetrics.FastGenByArgs(err.Error())
}
// The default workload to calculate the RU capacity.
if e.WorkloadType == ast.WorkloadNone {
e.WorkloadType = ast.TPCC
}
baseCost, ok := workloadBaseRUCostMap[e.WorkloadType]
if !ok {
return errors.Errorf("unknown workload '%T'", e.WorkloadType)
}
if totalTiDBCPUQuota/baseCost.tidbToKVCPURatio < totalKVCPUQuota {
totalKVCPUQuota = totalTiDBCPUQuota / baseCost.tidbToKVCPURatio
}
ruPerKVCPU := float64(ruCfg.ReadBaseCost)*float64(baseCost.readReqCount) +
float64(ruCfg.CPUMsCost)*baseCost.kvCPU*1000 + // convert to ms
float64(ruCfg.ReadBytesCost)*float64(baseCost.readBytes) +
float64(ruCfg.WriteBaseCost)*float64(baseCost.writeReqCount) +
float64(ruCfg.WriteBytesCost)*float64(baseCost.writeBytes)
quota := totalKVCPUQuota * ruPerKVCPU
req.AppendUint64(0, uint64(quota))
return nil
}
复制代码
阅读该函数得知,若要计算最终的 quota 需要如下几类输入:
各组件的 CPU 配置参数:
在本文,因为示例集群主机配置为 2C,因此该值均为 2。
负载类型及相关参数值:
以上是针对 TPCC 负载的参数值。
RU 和资源的默认换算关系参数:
有了上述 3 类输入,便可以将参数值带入以下逻辑中
if totalTiDBCPUQuota/baseCost.tidbToKVCPURatio < totalKVCPUQuota {
// 2 / 0.6= 3.33 < 2 结果为 false,因此不执行下面这一行的逻辑
totalKVCPUQuota = totalTiDBCPUQuota / baseCost.tidbToKVCPURatio
}
ruPerKVCPU := float64(ruCfg.ReadBaseCost)*float64(baseCost.readReqCount) +
float64(ruCfg.CPUMsCost)*baseCost.kvCPU*1000 + // convert to ms
float64(ruCfg.ReadBytesCost)*float64(baseCost.readBytes) +
float64(ruCfg.WriteBaseCost)*float64(baseCost.writeReqCount) +
float64(ruCfg.WriteBytesCost)*float64(baseCost.writeBytes)
quota := totalKVCPUQuota * ruPerKVCPU
//将上述三类参数带入上面公式,可计算出结果最终估算RU为5739:
// ruPerKVCPU := float64(0.125)*float64(300) + =37.5
// float64(1. / 3)*0.15*1000 + =50
// float64(1. / (64 * 1024))*float64(1048576/2) + =8
// float64(1)*float64(1750) + =1750
// float64(1. / 1024)*float64(1048576) =1024
// quota :=2869.5*2 =5739
复制代码
下面我们通过命令验证下上述计算结果是否符合预期:
可以看到,通过命令输出与上面带入公式计算的结果一致。
3. 思考
1 基于硬件部署估算的方法仅限于在空集群下使用特定的模型对集群资源单元(RU)进行初步预估,线上集群不建议使用该功能。
2 从代码上看到当前的基于硬件的静态估算模式有限制。在上述获取 CPU 信息的逻辑中,并没有考虑以下三种情况:
TiDB 是否使用 NUMA;
TiDB 与其他组件混合部署;
集群单个组件所在的主机配置不一致。
因此在上述情况下,可能导致获取的 CPU 信息不准确。
评论