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 配置参数:
totalKVCPUQuota, err := getTiKVTotalCPUQuota(clusterInfo)
//totalKVCPUQuota 获取TiKV组件的总CPU核数
// 函数主要逻辑:通过读取 tikv_server_cpu_cores_quota 监控指标来获取单个TiKV组件的cpu核数,并与组件个数相乘。
totalTiDBCPUQuota, err := getTiDBTotalCPUQuota(clusterInfo)
//totalTiDBCPUQuota 获取TiDB组件的总CPU核数
// 函数主要逻辑:通过执行untime.GOMAXPROCS(0) 函数来获取TiDB组件所在的操作系统cpu核数,并与组件个数相乘。
在本文,因为示例集群主机配置为2C,因此该值均为2。
- 负载类型及相关参数值:
ast.TPCC: {
tidbToKVCPURatio: 0.6,
kvCPU: 0.15,
readBytes: units.MiB / 2,
writeBytes: units.MiB,
readReqCount: 300,
writeReqCount: 1750,
}
以上是针对TPCC负载的参数值。
- RU 和资源的默认换算关系参数:
const (
// 1 RU = 8 storage read requests
defaultReadBaseCost = 1. / 8 // 0.125
// 1 RU = 2 storage read batch requests
defaultReadPerBatchBaseCost = 1. / 2 // 0.5
// 1 RU = 1 storage write request
defaultWriteBaseCost = 1
// 1 RU = 1 storage write batch request
defaultWritePerBatchBaseCost = 1
// 1 RU = 64 KiB read bytes
defaultReadCostPerByte = 1. / (64 * 1024)
// 1 RU = 1 KiB written bytes
defaultWriteCostPerByte = 1. / 1024
// 1 RU = 3 millisecond CPU time
defaultCPUMsCost = 1. / 3
// Because the resource manager has not been deployed in microservice mode,
// do not enable this function.
defaultDegradedModeWaitDuration = time.Duration(0)
defaultAvgBatchProportion = 0.7
)
有了上述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信息不准确。