本文来自OPPO互联网技术团队,转载请注名作者。同时欢迎关注我们的公众号:OPPO_tech,与你分享OPPO前沿互联网技术及活动。

Presto作为一个大数据场景下的交互式查询引擎,在OPPO线上已经正常提供一年的查询服务了。

从刚开始,仅有国内几台服务器,到现在服务已经覆盖了国内外多个地区的大部分交互式查询,其中仅国内服务器已经达到了150台。

随着用户使用规模的扩大,大家在Presto资源分配方面有一些疑惑,比较有代表性的问题如下:

  1. 随着Presto使用的业务团队的增多,当有团队A的任务比较重要,不希望执行的时候没有了查询的资源,应该如何重点保证?
  2. 集群资源负载比较高时,我和另外一个同事同时提交了一个查询,一开始我俩都在排队,当有空闲资源时,谁的查询会先获得资源?
  3. 对于Adhoc类型的查询,我不想得喝完咖啡才能拿到结果;而批处理的查询,我明天再拿结果就好,如何根据不同的sql类型,做更合理的资源分配?

今天我们将介绍Presto的资源组机制,一一解答以上的问题。

1. 何为Presto资源组

Presto的资源组机制,是从资源分配的角度来控制集群的整体查询负载。

Presto会在集群整体资源下开辟多个资源组,每一个提交的查询都会分配到一个特定的资源组执行。

在特定资源组A开启一个新的查询B之前,会检查当前A的资源负载是否超过了集群给A分配的资源量;如果已经超过了,资源组机制会阻塞新到的查询B,使其处于排队状态甚至直接拒绝。

细说概念:

资源可以分成CPU,内存,带宽,磁盘等维度,Presto资源组中定义了哪些资源?

目前的资源组所控制的主要是内存和CPU两个维度。

小小疑惑:

资源组是在查询开始之前的控制机制,一旦一个查询已经开始执行了,就不会受资源组的管控了,那岂不是太过于奔放了吗?

其实,这个问题不用担心。Presto除了资源组机制外,还有一些其他的机制(如最大查询可用内存,最大查询时间等);定期检查查询的执行情况,以保证整体集群运行的稳定性。

2. 具体怎么玩?

要想在Presto集群中使用资源组功能,我们只需要在Presto Coordinator节点安装目录etc下新建一个文件 resource-groups.json,然后将resource-groups.config-file 指向资源组配置文件的路径,比如:

1. resource-groups.configuration-manager=file 
2. resource-groups.config-file=etc/resource-groups.json

然后在这个json文件中作以下两部分配置即可:

2.1 资源组主要配置项

name(必须):

特定资源组名称;

maxQueued(必须):

排队任务的最大数量,当达到此阈值后,新的任务将被拒绝;

hardConcurrencyLimit(必须):

任何时刻处于"RUNNING"状态的查询的最大数量;

softMemoryLimit(必须):

这个资源组最大内存使用量,当达到此阈值后,新任务进入排队。可以指定为一个绝对值(如100GB),也可以指定对集群内存总量的百分比(如60%);

softCpuLimit(可选):

一个周期里可以使用cpu的时间,hardCpuLimit也必须指定,在达到该阈值后,该资源组内占据最大CPU资源的查询的CPU资源会被减少;

hardCpuLimit(可选):

一个周期里可以使用的cpu时间,在达到该阈值后,新的查询会进行排队而非直接执行;

schedulingPolicy(可选) :

指定查询从排队到运行状态的调度策略。

【这里解决用户问题2,参照不同的调度策略,可能会有不同的资源分配顺序】

主要有以下类型:

fair(default):

当一个资源组下,有几个子资源组都同时有排队的查询,这些子资源组间按照定义的顺序,轮流获得资源,同一个子资源组的查询按照先来先执行的规则获取资源;

weighted_fair

采取这种策略的每一个资源组会配置一个属性schedulingWeight,每个子资源组会计算一个比值:

当前子资源组查询数量/schedulingWeight,比值越小的子资源组越先得到资源;

weighted

默认值为1,子资源组的schedulingWeight越大,越先得到资源;

query_priority

所有的子资源组都要配置为 query_priority ,排队的查询严格按照指定的query_priority大小顺序来进行获取资源。

2.2 资源组选择器

user(可选):

匹配用户名;

source(可选):

匹配连接源,如cli、jdbc、pyhive等;

queryType(可选):

匹配任务类型;

clientTags(可选):

tag列表,每个tag必须在用户提交任务的tag列表里;

group(必须):

这些任务运行的组。

【这里解决用户问题3,可以对不同的查询类型queryType,比如EXPLAIN、INSERT、SELECT和DATA_DEFINITION等类型,匹配到不同的资源组,分配不同的资源,来执行查询】

3. 举一个栗子

为了加深大家的理解,参照Presto官网的资源组栗子可做以下简单图描述:

https://prestosql.io/docs/cur...

  1. 对于global资源组而言,最多可同时运行100个查询,有1000查询处于排队状态,在它下面有三个子资源组:data_definition、adhoc 和 pipeline;
  2. pipeline资源组下每一个用户最多可同时运行5个查询,占用pipeline资源组50%的内存资源,其组内默认采用fair的调度策略,所以是按照先来先执行的顺序执行;
  3. 为了充分利用集群资源,各个子资源组的内存配额的总和可大于父资源组,比如global资源组(80%)+admin(100%)=180%>100% 。

【这里解决用户问题1,当我们保证子资源组的占比和小于等于100%时,可保证某一个队列的资源总是有的,不被其他资源组抢占,类似静态化分资源】

再来看看资源组选择器

1.如上每一个大括号代表一个匹配资源组的选择器selector,这里一共配置了5个选择器以匹配上面的5个资源组:

admin
global.data_definition
global.pipeline.pipeline_${USER}、global.adhoc.bi-${toolname}.${USER
global.adhoc.other.${USER}

2.要全部满足当前selector全部条件,才可放进当前队列执行。比如amy用户使用jdbc方式提交的查询,如果没有配置clientTags,是不能够分配到资源组global.adhoc.bi-${toolname}.${USER}对应的资源;

3.当一个查询能同时满足两个seletor时,会匹配第一个满足要求的seletor。比如bob用户提交一个source为pipeline的DATA_DEFINITION类型的job,只会匹配到资源组admin对应的资源,而非global.data_definition对应的资源;

4.当前4个seletor都没有匹配上,会使用最后一个seletor指定的资源组global.adhoc.other.${USER}的资源。该资源组相当于起到一个默认资源组的作用,如果没有设置默认资源组,而又不符合其他资源组选择器条件则会被拒绝执行。

4. 小结

Presto资源组可认为是实现了一个弱资源限制和隔离功能。

我们可以为每个资源组指定队列大小、并发大小、内存使用大小以及CPU资源量。加深对资源组的理解,为每个资源组设置合理的资源参数,一方面可以降低不同业务之间的影响,另一方面也有助于系统减少OOM问题。

参考文献:

https://prestosql.io/docs/cur...

https://www.qubole.com/blog/c...


OPPO数智技术
612 声望950 粉丝