更新时间:2024-07-24 GMT+08:00

配置HetuEngine资源组

资源组介绍

资源组机制从资源分配的角度控制实例的整体查询负载,并可以对查询实施排队策略。可以在一个计算实例资源下创建多个资源组,并且每个提交的查询将分配给一个特定的资源组执行。在资源组执行新查询之前,将检查当前资源组的资源负载是否超过实例分配给它的资源量。如果超过,则将阻止新到达的查询,使其处于排队状态,甚至直接拒绝它。

资源组使用场景

通过资源组可以实现计算实例内的资源管理。对不同用户、不同查询分配不同的资源组,可以起到资源隔离的作用,避免单个用户或查询独占计算实例的资源,也能通过资源组之间的权重优先级配置保障重要任务优先执行。典型资源组使用场景如表1所示。

表1 典型资源组使用场景

典型场景

解决方案

随着使用计算实例的业务团队的增加,当某个团队的任务更加重要并且不想执行查询时没有资源。

每个团队分配一个指定的资源组;重要任务分配到资源较多的资源组;保证子资源组的占比和小于等于100%时,可保证某一个队列的资源不被其他资源组抢占,类似于静态化分资源。

当实例资源负载很高时,两个用户同时提交一个查询。一开始,两个查询都在排队。当有空闲资源时,可以调度特定用户的查询首先获取到资源。

两个用户分配不同的资源组,重要的任务可以分配到权重高或优先级高的资源组,调度策略由schedulingPolicy配置,不同的调度策略,会有不同的资源分配顺序。

对于即席查询和批量查询,可以根据不同的SQL类型进行更合理的资源分配。

可以对不同的查询类型,比如EXPLAIN、INSERT、SELECT和DATA_DEFINITION等类型,匹配到不同的资源组,分配不同的资源来执行查询。

启用资源组

在创建计算实例的时候,增加参数文件“resource-groups.json”的自定义配置参数,具体操作请参见3.e

资源组属性

资源组属性配置请参见表2

表2 资源组属性

配置项

必选/可选

配置说明

name

必选

资源组名称。

maxQueued

必选

最大排队查询数,当达到此阈值后,新的查询将被拒绝。

hardConcurrencyLimit

必选

最大运行查询数。

softMemoryLimit

可选

资源组最大内存使用量,当达到此阈值后,新任务进入排队;可以指定为固定值(如,10GB)或百分比(如,集群内存的10%)。

softCpuLimit

可选

在一个周期内(参见全局属性的cpuQuotaPeriod参数)可以使用CPU的时间,必须同时指定hardCpuLimit参数,在达到该阈值后,该资源组内占据最大CPU资源的查询的CPU资源会被减少。

hardCpuLimit

可选

在一个周期内可以使用的最大CPU时间。

schedulingPolicy

可选

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

  • fair(default)

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

  • weighted_fair

    采取这种策略的每一个资源组会配置一个属性schedulingWeight,每个子资源组会计算一个比值:当前子资源组查询数量/schedulingWeight。比值越小的子资源组越先得到资源。

  • weighted

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

  • query_priority

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

schedulingWeight

可选

该分组的权重,见schedulingPolicy,默认为1。

jmxExport

可选

如果为true,则组统计信息将被导出到JMX中进行监控,默认为false。

subGroups

可选

子分组列表。

killPolicy

可选

当查询提交给Worker后,如果总内存使用量超过softMemoryLimit,可选择一种策略终止正在运行的查询,策略如下所示:

  • no_kill(默认值):不终止查询。
  • recent_queries:根据执行顺序的倒序终止查询。
  • oldest_queries:根据执行顺序终止查询。
  • finish_percentage_queries:根据查询执行百分比终止查询。执行百分比最小的查询将首先被终止。high_memory_queries:根据内存使用量终止查询。具有较高内存使用量的查询将首先被终止,以便在查询终止次数最少的情况下,释放更多内存。当两个查询的内存使用量都在限制的10%以内,则进度慢(执行的百分比)的查询被终止,同时两个查询在完成百分比方面的差异在5%以内,则内存使用量大的查询被终止。

选择器规则

选择器按顺序进行匹配,将使用第一个匹配到的资源组,一般来说建议配置一个默认资源组,如果没有设置默认资源组,而又不符合其他资源组选择器条件则查询会被拒绝。选择器规则参数配置请参见表3

表3 选择器规则

配置项

必选/可选

配置说明

user

可选

匹配用户名的正则表达式。

source

可选

匹配请求源,参见选择器属性的配置中--source选项的配置值。

queryType

可选

配置任务类型:

  • DATA_DEFINITION:更改/创建/删除模式/表/视图的元数据的查询,以及管理预准备语句、权限、会话和事务的查询。
  • DELETE:DELETE查询。
  • DESCRIBE:DESCRIBE、DESCRIBE INPUT、DESCRIBE OUTPUT和SHOW查询。
  • EXPLAIN:EXPLAIN查询。
  • INSERT:插入和CREATE TABLE AS查询。
  • SELECT:SELECT查询。

clientTags

可选

匹配客户端标签,每个标签都必须在用户提交任务的标签列表里,参见选择器属性的配置中--client-tags选项的配置值。

group

必选

在其中运行查询的资源组。

全局属性

全局属性配置请参见表4

表4 全局属性

配置项

必选/可选

配置说明

cpuQuotaPeriod

可选

CPU配额生效的时间段,与资源组属性的softCpuLimit以及hardCpuLimit结合使用。

选择器属性的配置

数据源名称(source)可设置如下:

  • CLI:使用--source选项。
  • JDBC:在Connection实例上设置ApplicationName客户端信息属性。

客户端标签(clientTags)的设置方式如下:

  • CLI:使用--client-tags选项。
  • JDBC:在Connection实例上设置ClientTags client info属性。

配置示例

图1 配置示例

图1所示:

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

在下面的示例配置中,存在多个资源组,其中一些资源组是模板。模板允许HetuEngine管理员动态构建资源组树。例如,在pipeline_${USER}组中,${USER}将扩展为提交查询的用户名称。${SOURCE}也支持,后续会扩展到提交查询的来源。也可以在source表达式和user正则表达式中使用自定义命名变量。

资源组选择器示例如下:

"selectors": [{
	"user": "bob",
	"group": "admin"
},
{
	"source": ".*pipeline.*",
	"queryType": "DATA_DEFINITION",
	"group": "global.data_definition"
},
{
	"source": ".*pipeline.*",
	"group": "global.pipeline.pipeline_${USER}"
},
{
	"source": "jdbc#(?<toolname>.*)",
	"clientTags": ["hipri"],
	"group": "global.adhoc.bi-${toolname}.${USER}"
},
{
	"group": "global.adhoc.other.${USER}"
}]

有四个选择器用于定义在哪个资源组中运行查询:

  • 第一个选择器匹配来自bob的查询,并将它们放在admin组中。
  • 第二个选择器匹配来自包括pipeline的源名称的所有数据定义(DDL)查询,并将它们放在global.data_definition组中。这有助于减少此类查询的排队时间,因为它们预计速度很快。
  • 第三个选择器匹配来自包括pipeline的源名称的查询,并将它们放在global.pipeline组下动态创建的单用户管道组中。
  • 第四个选择器匹配来自BI工具的查询,BI工具有一个源与正则表达式jdbc#(?.*)匹配,并且客户端提供的标签是hi-pri的超集。这些查询被放置在global.adhoc组下动态创建的子组中。动态子组将基于命名变量toolname创建,该命名变量从源的正则表达式中提取。假设有一个源为jdbc#powerfulbi,用户为kayla,客户端标签为hipri和fast的查询。此查询将被路由到global.adhoc.bi-powerfulbi.kayla资源组。
  • 最后一个选择器是一个默认选择器,它将所有尚未匹配的查询放入该资源组。

这些选择器一起实现以下策略:

  • bob是HetuEngine管理员用户,可以同时运行50个查询。查询将根据用户提供的优先级运行。
  • 对于剩余用户:
    • 同时运行的查询总数不能超过100个。
    • 使用源pipeline最多可以运行5个并发的DDL查询。查询按FIFO顺序运行。
    • 非DDL查询将在global.pipeline组下运行,总并发数为45,每用户并发数为5。查询按FIFO顺序运行。
    • 对于BI工具,每个工具最多可以运行10个并发查询,每个用户最多可以运行3个。如果总需求超过10个限制,运行查询最少的用户将获得下一个并发槽。这项策略使得资源争夺时更加公平。
    • 所有剩余的查询都放在global.adhoc.other下的每个用户组中,该组行为类似。

查询匹配选择器的说明:

  • 如上每一个大括号代表一个匹配资源组的选择器selector,这里一共配置了5个选择器以匹配上面的5个资源组:
    admin 
    global.data_definition 
    global.pipeline.pipeline_${USER}
    global.adhoc.bi-${toolname}.${USER}
    global.adhoc.other.${USER}
  • 要全部满足当前selector全部条件,才可放进当前队列执行。比如amy用户使用jdbc方式提交的查询,如果没有配置clientTags,是不能够分配到资源组global.adhoc.bi-${toolname}.${USER}对应的资源;
  • 当一个查询能同时满足两个selector时,会匹配第一个满足要求的selector。比如bob用户提交一个source为pipeline的DATA_DEFINITION类型的job,只会匹配到资源组admin对应的资源,而非global.data_definition对应的资源;
  • 当前4个selector都没有匹配上,会使用最后一个selector指定的资源组global.adhoc.other.${USER}的资源。该资源组相当于起到一个默认资源组的作用,如果没有设置默认资源组,而又不符合其他资源组选择器条件则会被拒绝执行。

    如下是完整样例:

    {
    	"rootGroups": [{
    		"name": "global",
    		"softMemoryLimit": "80%",
    		"hardConcurrencyLimit": 100,
    		"maxQueued": 1000,
    		"schedulingPolicy": "weighted",
    		"jmxExport": true,
    		"subGroups": [{
    			"name": "data_definition",
    			"softMemoryLimit": "10%",
    			"hardConcurrencyLimit": 5,
    			"maxQueued": 100,
    			"schedulingWeight": 1
    		},
    		{
    			"name": "adhoc",
    			"softMemoryLimit": "10%",
    			"hardConcurrencyLimit": 50,
    			"maxQueued": 1,
    			"schedulingWeight": 10,
    			"subGroups": [{
    				"name": "other",
    				"softMemoryLimit": "10%",
    				"hardConcurrencyLimit": 2,
    				"maxQueued": 1,
    				"schedulingWeight": 10,
    				"schedulingPolicy": "weighted_fair",
    				"subGroups": [{
    					"name": "${USER}",
    					"softMemoryLimit": "10%",
    					"hardConcurrencyLimit": 1,
    					"maxQueued": 100
    				}]
    			},
    			{
    				"name": "bi-${toolname}",
    				"softMemoryLimit": "10%",
    				"hardConcurrencyLimit": 10,
    				"maxQueued": 100,
    				"schedulingWeight": 10,
    				"schedulingPolicy": "weighted_fair",
    				"subGroups": [{
    					"name": "${USER}",
    					"softMemoryLimit": "10%",
    					"hardConcurrencyLimit": 3,
    					"maxQueued": 10
    				}]
    			}]
    		},
    		{
    			"name": "pipeline",
    			"softMemoryLimit": "80%",
    			"hardConcurrencyLimit": 45,
    			"maxQueued": 100,
    			"schedulingWeight": 1,
    			"jmxExport": true,
    			"subGroups": [{
    				"name": "pipeline_${USER}",
    				"softMemoryLimit": "50%",
    				"hardConcurrencyLimit": 5,
    				"maxQueued": 100
    			}]
    		}]
    	},
    	{
    		"name": "admin",
    		"softMemoryLimit": "100%",
    		"hardConcurrencyLimit": 50,
    		"maxQueued": 100,
    		"schedulingPolicy": "query_priority",
    		"jmxExport": true
    	}],
    	"selectors": [{
    		"user": "bob",
    		"group": "admin"
    	},
    	{
    		"source": ".*pipeline.*",
    		"queryType": "DATA_DEFINITION",
    		"group": "global.data_definition"
    	},
    	{
    		"source": ".*pipeline.*",
    		"group": "global.pipeline.pipeline_${USER}"
    	},
    	{
    		"source": "jdbc#(?<toolname>.*)",
    		"clientTags": ["hipri"],
    		"group": "global.adhoc.bi-${toolname}.${USER}"
    	},
    	{
    		"group": "global.adhoc.other.${USER}"
    	}],
    	"cpuQuotaPeriod": "1h"
    }