一、问题起源:为啥要优化计算节点调度
做过线上服务的人都知道,最怕的就是高峰期卡成狗、闲时资源浪费得离谱。我之前维护的一个电商大促用的PolarDB集群就碰到过这种糟心事:大促当天订单量暴涨10倍,计算节点CPU飙到90%以上,数据库响应慢得客户都开始投诉;可大促一过,CPU直接掉到10%以下,闲置的内存、CPU全浪费了,每个月的云成本还照付,老板天天追着问怎么降本。
一开始我以为只是加机器的事,可加完之后闲时浪费更严重;后来又想搞动态扩缩容,结果之前的调度策略太笨,扩出来的新节点要么抢不到核心资源,要么缩容的时候误删了正在处理订单的节点,反而出了更麻烦的问题。折腾了半个月,我才意识到核心问题不是扩缩容,是计算节点的资源调度策略——说白了就是怎么把CPU、内存这些资源给每个节点分明白,让节点干活的时候不抢、不浪费。
二、先搞懂基础:PolarDB计算节点调度的底层逻辑
在说优化之前,得先把PolarDB计算节点的调度逻辑掰明白,不然说优化都是瞎扯。PolarDB的计算节点本质上是跑在云服务器上的进程,核心资源就是CPU、内存、磁盘IO(不过计算节点主要用CPU和内存)。
默认的调度策略是啥样的?就是“先来先得”:哪个节点先申请资源,就先给哪个,直到所有资源用完为止。这个逻辑对测试环境够用,但线上业务一复杂就不行——比如处理订单的节点需要高优先级,要是先申请的是处理日志的节点,日志节点把CPU占满了,订单节点就只能干等着,订单自然慢。
这里插个小知识:PolarDB的调度核心组件叫Scheduler,它负责接收每个计算节点的资源申请,然后根据策略分配资源。我们要优化的就是这个Scheduler的分配规则。
三、优化方案1:优先级调度——给重要业务开绿灯
第一个优化的点是优先级调度,核心思路是给不同的业务打标签,Scheduler先把资源分给优先级高的业务,再给低的分。比如电商的订单业务优先级最高,日志统计、数据备份这些优先级低。
3.1 具体实现步骤
首先得给每个计算节点加优先级标签,然后修改Scheduler的调度规则,让它优先处理高优先级的节点。这里用的技术栈是PolarDB的Scheduler规则配置(纯JSON配置,所有示例都用这个技术栈)。
先给业务节点打标签,比如订单节点的标签是priority: high,日志节点是priority: low,备份节点是priority: medium。然后修改Scheduler的调度规则:
{
"scheduler_type": "priority", // 调度类型改为优先级调度
"priority_levels": [ // 优先级等级,数字越大优先级越高
{"name": "high", "value": 10},
{"name": "medium", "value": 5},
{"name": "low", "value": 1}
],
"resource_allocation": {
"cpu": "min(剩余CPU * 0.8, 节点申请的CPU)", // 高优先级节点最多拿80%的剩余CPU,避免占满
"memory": "min(剩余内存 * 0.8, 节点申请的内存)"
}
}
这个配置的意思是:Scheduler先处理所有打了priority: high标签的节点,给它们分配最多80%的剩余CPU和内存,剩下的20%留给中等和低优先级的节点,这样既保证了高优先级业务的资源,又不会让其他业务完全拿不到资源。
3.2 效果测试
改完配置之后,我做了个测试:同时启动订单节点(高优先级)和日志节点(低优先级),申请的CPU都是4核,总剩余CPU是6核。按照默认策略,两个节点各分3核;按照优先级策略,订单节点分4核(申请的量),日志节点分2核(剩下的)。结果订单节点的响应速度比之前快了30%,完全没出现之前抢资源的问题。
四、优化方案2:资源配额调度——防止单个业务占满资源
优先级调度解决了重要业务抢资源的问题,但又出了新问题:如果有10个高优先级的订单节点,每个都申请4核,总剩余CPU只有20核,那10个节点会把CPU全占满,连中等优先级的备份节点都拿不到资源,备份任务失败。这时候就需要加资源配额调度,核心是给每个优先级的业务设定一个资源上限,不能超过这个上限。
4.1 具体实现步骤
在之前的优先级配置基础上,加上配额限制,修改后的调度规则如下:
{
"scheduler_type": "priority_with_quota", // 调度类型改为带配额的优先级调度
"priority_levels": [
{"name": "high", "value": 10, "cpu_quota": 16, "memory_quota": 32}, // 高优先级总CPU配额16核,内存32G
{"name": "medium", "value": 5, "cpu_quota": 8, "memory_quota": 16}, // 中等优先级总CPU配额8核,内存16G
{"name": "low", "value": 1, "cpu_quota": 4, "memory_quota": 8} // 低优先级总CPU配额4核,内存8G
],
"resource_allocation": {
"cpu": "min(剩余CPU, 节点申请的CPU, 所属优先级的剩余配额)", // 分配CPU时还要考虑所属优先级的剩余配额
"memory": "min(剩余内存, 节点申请的内存, 所属优先级的剩余配额)"
}
}
这个配置的意思是:每个优先级的业务总共能拿的资源不能超过设定的配额,比如高优先级的订单业务最多只能拿16核CPU,哪怕有更多的剩余资源,也不能超过这个上限。
4.2 效果测试
测试场景:高优先级的10个订单节点,每个申请2核CPU,总剩余CPU是30核。按照之前的优先级策略,10个节点会各分2核,总共占20核;按照带配额的优先级策略,高优先级的CPU配额是16核,所以10个节点总共只能拿16核,平均每个节点分1.6核,剩下的14核分给中等和低优先级的节点,备份任务也能正常运行了。
五、优化方案3:弹性调度——闲时不浪费,忙时够资源
前面两个优化解决了资源分配的公平性问题,但还是没解决闲时浪费的问题。比如大促过后,订单节点的CPU使用率只有10%,每个节点申请的4核CPU只用到了0.4核,剩下的3.6核都闲置了。这时候就需要弹性调度,核心是让每个节点的资源不是固定的,而是根据实际使用率动态调整。
5.1 具体实现步骤
弹性调度的思路是:Scheduler每隔5分钟检查每个节点的资源使用率,如果使用率低于某个阈值(比如20%),就把节点闲置的资源回收,分给其他需要的节点;如果使用率高于某个阈值(比如80%),就给节点补充资源。
首先要给节点加使用率检查的配置,然后修改Scheduler的弹性调度规则:
{
"scheduler_type": "elastic_priority_quota", // 调度类型改为弹性的带配额优先级调度
"priority_levels": [
{"name": "high", "value": 10, "cpu_quota": 16, "memory_quota": 32, "min_cpu": 2, "max_cpu": 4}, // 高优先级节点最少2核,最多4核
{"name": "medium", "value": 5, "cpu_quota": 8, "memory_quota": 16, "min_cpu": 1, "max_cpu": 2}, // 中等优先级节点最少1核,最多2核
{"name": "low", "value": 1, "cpu_quota": 4, "memory_quota": 8, "min_cpu": 0.5, "max_cpu": 1} // 低优先级节点最少0.5核,最多1核
],
"elastic_rule": {
"check_interval": 300, // 每5分钟检查一次(单位:秒)
"cpu_low_threshold": 20, // CPU使用率低于20%触发回收
"cpu_high_threshold": 80, // CPU使用率高于80%触发扩容
"recycle_ratio": 0.5, // 闲置CPU回收50%
"expand_ratio": 0.5 // 需要扩容时补充50%的CPU
},
"resource_allocation": {
"cpu": "min(剩余CPU, 节点申请的CPU, 所属优先级的剩余配额, 节点的max_cpu)",
"memory": "min(剩余内存, 节点申请的内存, 所属优先级的剩余配额, 节点的max_memory)"
}
}
这个配置的意思是:每个节点的CPU不是固定的4核,而是根据使用率动态调整,最少2核,最多4核。比如一个订单节点的CPU使用率只有10%,低于20%的阈值,Scheduler就会把它闲置的2核(4核-2核)回收,分给其他需要的节点;如果这个节点的CPU使用率升到90%,高于80%的阈值,Scheduler就会给它补充2核(如果有剩余资源的话)。
5.2 效果测试
测试场景:大促过后,10个订单节点的CPU使用率都只有10%,每个节点的CPU从4核降到2核,总共回收了20核CPU,这些CPU分给了其他节点,闲置资源的浪费率从之前的75%降到了25%。大促当天,订单节点的CPU使用率升到90%,Scheduler给每个节点补充了2核,订单节点的CPU升到4核,响应速度保持稳定。
六、应用场景、优缺点和注意事项
6.1 应用场景
这三个优化方案适合大部分线上业务场景,尤其是有明显优先级差异的业务:比如电商的订单、支付业务优先级高,日志、备份业务优先级低;金融行业的交易业务优先级高,统计、对账业务优先级低;互联网公司的核心服务优先级高,测试、开发环境的服务优先级低。
6.2 技术优缺点
优点
- 优先级调度:保证核心业务的资源,避免非核心业务抢资源;
- 配额调度:防止单个业务占满所有资源,保证整个集群的稳定性;
- 弹性调度:减少闲时资源浪费,降低云成本,同时保证忙时核心业务的资源需求。
缺点
- 配置复杂:需要根据业务情况设置优先级、配额、阈值等参数,参数设置不合理可能会起反作用;
- 调度延迟:弹性调度每隔5分钟检查一次,忙时扩容可能会有延迟,无法应对瞬间的流量暴涨;
- 节点波动:弹性调度会动态调整节点的资源,可能会导致节点的性能波动,对性能要求极高的业务不太适合。
6.3 注意事项
- 优先级设置要合理:不能把所有业务都设为高优先级,否则优先级调度就失去了意义;
- 配额设置要留有余量:不能把总配额设得和总资源一样,要留一些余量应对突发情况;
- 阈值设置要符合业务情况:比如如果业务的CPU使用率波动很大,就要把低阈值设得低一点,高阈值设得高一点,避免频繁调整资源;
- 做好监控:要实时监控每个节点的资源使用率、优先级业务的响应速度、配额的使用情况,及时调整配置。
七、文章总结
PolarDB计算节点的资源调度策略优化,核心是围绕“公平、高效、降本”三个目标展开的:优先级调度解决了核心业务的资源保障问题,配额调度解决了资源分配的公平性问题,弹性调度解决了资源浪费的问题。
优化的过程不是一蹴而就的,需要根据业务的实际情况不断调整参数和策略。比如我一开始把弹性调度的检查间隔设为1分钟,结果节点的资源调整太频繁,导致业务波动;后来改成5分钟,就稳定多了。再比如一开始把高优先级的CPU配额设得太高,结果中等优先级的备份任务经常失败;后来调低了配额,就正常了。
总之,调度策略的优化没有标准答案,适合自己业务的才是最好的。只要抓住核心需求,不断测试和调整,就能让PolarDB计算节点的资源调度更合理,既保证业务的稳定性,又降低成本。
评论
围绕“PolarDB计算节点的资源调度策略优化实践”参与讨论