资源组¶
资源组对资源使用设置限制,并可以对在其中运行的查询强制执行排队策略,或将其资源分配给子组。一个查询属于单个资源组,并从该组(及其祖先)消耗资源。除了对排队查询的限制外,当资源组用完资源时,不会导致正在运行的查询失败;相反,新的查询将被排队。资源组可以有子组,也可以接受查询,但不能同时执行两者。
在 PrestoDB 中,资源组是管理查询执行和资源分配的强大工具。它们允许管理员控制资源如何在 Presto 集群中分配和使用。
资源使用限制¶
资源组可以对资源使用设置限制,例如 CPU 时间、内存使用量或总查询数量。这在多租户环境中特别有用,您希望确保没有单个用户或查询独占系统资源。
资源消耗¶
一个查询属于单个资源组,它从该组及其父组消耗资源。如果资源组用完某种资源,不会导致正在运行的查询失败。相反,新的查询将被排队,直到资源再次可用。
子组和查询接受¶
子组允许分层资源分配,其中每个子组都可以有自己的资源限制和排队策略。直接接受查询的资源组是叶组,它使用分配的资源执行查询。
资源组和相关的选择规则由管理器配置,管理器是可插拔的。Presto 资源管理可以通过两种方式完成
基于文件的资源管理¶
在基于文件的资源管理器中,有关资源组的配置信息存储在 JSON 文件中。该文件包含所有资源组的定义以及选择它们的规则。配置文件在 Presto 服务器启动时加载并使用。对文件的任何更改都需要重新启动 Presto 服务器才能生效。
基于数据库的资源管理¶
在基于数据库的资源管理器中,有关资源组的配置信息存储在关系数据库中。数据库包含所有资源组的定义以及选择它们的规则。与基于文件的资源管理器不同,对数据库中配置的更改会立即生效,不需要重新启动 Presto 服务器。
两种方法都有其优缺点。基于文件的资源管理更易于设置,但灵活性较差,而基于数据库的资源管理更难设置,但提供了更大的灵活性,并支持动态更改。
文件资源组管理器¶
PrestoDB 中的文件资源组管理器是一种使用 JSON 配置文件管理资源的方式。该文件包含所有资源组的定义以及为给定查询选择适当资源组的规则。
要设置基于文件的资源组管理器
创建文件
etc/resource-groups.properties
。在
etc/resource-groups.properties
中,使用以下代码示例将resource-groups.configuration-manager
属性设置为file
resource-groups.configuration-manager=file
在
etc
中创建一个名为resource-groups.json
的 JSON 文件。该文件应包含资源组的定义。每个资源组都可以指定诸如最大内存、最大排队查询和最大运行查询之类的内容。有关创建资源组定义的信息,请参见 资源组属性。有关 resource-groups.json 文件的示例,请参见 文件资源组管理器。
在
etc/resource-groups.properties
中,添加一行,使用以下代码示例指定 JSON 文件的位置。将resource-groups.config-file
属性设置为<file_path>
,其中<file_path>
是 JSON 文件的路径。resource-groups.config-file=etc/resource-groups.json
重新启动 Presto 服务器。新的资源组在重新启动后立即生效。
数据库资源组管理器¶
PrestoDB 中的数据库资源组管理器是一种使用关系数据库管理资源的方式。
要设置基于数据库的资源组管理器
创建文件
etc/resource-groups.properties
。将
resource-groups.configuration-manager
属性设置为db
。设置关系数据库:数据库应可被 Presto 访问。它将用于存储资源组的配置。
为资源组和选择规则创建表:您需要在数据库中创建表,用于存储资源组的定义以及为给定查询选择适当资源组的规则。
要在 Presto 配置中指定数据库,请在
etc/resource-groups.properties
中添加一行,指定数据库的 JDBC URL:resource-groups.config-db-url = <jdbc_url>
,其中<jdbc_url>
是数据库的 JDBC URL。注意:目前仅支持 MySQL。
etc/resource-groups.properties
应类似于以下示例
resource-groups.configuration-manager=db
resource-groups.config-db-url=jdbc:mysql://127.0.0.1:3306/resource_groups?user=<user>&password=<password>
将 <user>
和 <password>
替换为实际的用户名和密码。
使用数据库资源组管理器,对数据库中配置的更改会立即生效,不需要重新启动 Presto 服务器。这允许更灵活地对资源组配置进行动态更改。
资源组配置必须通过表 resource_groups_global_properties
、resource_groups
和 selectors
填充。如果 Presto 启动时任何表都不存在,它们会自动创建。
表 selectors
中的规则按 priority
字段中的值降序处理。
数据库资源组管理器属性¶
属性名称 |
描述 |
默认值 |
---|---|---|
|
用于加载配置的数据库 URL。 |
|
|
在刷新失败后,集群将继续接受查询的最长时间段,这会导致配置变得陈旧。 |
|
|
设置此标志将启用使用额外的 |
|
资源组属性¶
资源组使用一组属性定义,这些属性决定了资源的分配和使用方式。以下是可为资源组设置的关键属性
name
(必需):资源组的名称。这是必需属性。maxQueued
(必需): 资源组中可以排队的最大查询数。如果达到此限制,新查询将被拒绝。hardCpuLimit
(可选): 此组在一个时期内可以使用 CPU 时间的最大量。softMemoryLimit
(必需): 资源组可以使用内存的最大量。这可以以绝对值(例如“10GB”)或可用内存的百分比(例如“50%”)指定。hardConcurrencyLimit
(必需): 资源组可以同时运行的最大查询数。softConcurrencyLimit
(可选): 并发查询数的软限制。如果超过此限制,调度程序将尝试阻止新的查询启动,但不会强制正在运行的查询停止。softCpuLimit
(可选): 此组在一个时期内可以使用 CPU 时间的最大量(见cpuQuotaPeriod
),在对最大运行查询数施加惩罚之前。还必须指定hardCpuLimit
。schedulingPolicy
(可选): 确定如何在资源组内调度查询的策略。这可以设置为四个值之一fair
、weighted
、weighted_fair
或query_priority
fair
(默认): 排队的查询按先入先出顺序处理,并且如果子组有任何排队的查询,它们必须轮流启动新查询。weighted_fair
: 子组是根据其schedulingWeight
以及它们已经同时运行的查询数来选择的。子组的预期运行查询份额是根据所有当前符合条件的子组的权重计算的。与份额相比,并发性最小的子组将被选中以启动下一个查询。weighted
: 排队的查询是根据其优先级以随机方式选择的,优先级通过query_priority
{doc}session property </sql/set-session>
指定。子组被选中以按其schedulingWeight
启动新查询。query_priority
: 所有子组也必须配置为query_priority
。排队的查询严格按照其优先级进行选择。
schedulingWeight
(可选): 当父组使用weighted
调度策略时,资源组的权重。权重越高,组获得的父组资源份额就越大。jmxExport
(可选): 如果设置为true
,则资源组的统计信息将通过 JMX 导出。默认为false
。perQueryLimits
(可选): 指定每个资源组中的每个查询在被杀死之前可以消耗的最大资源。这些限制不会从父组继承。可以设置三种类型的限制executionTimeLimit
(可选): 指定查询执行的最大时间的绝对值(例如,1h
)。totalMemoryLimit
(可选): 指定查询可能消耗的最大分布式内存的绝对值(例如,1GB
)。cpuTimeLimit
(可选): 指定查询可能使用的最大 CPU 时间的绝对值(例如,1h
)。
workerPerQueryLimit
(可选): 指定每个查询必须可用的最小工作程序数。旨在用于工作程序数量随时间变化的弹性集群中。subGroups
(可选): 子组列表。资源组中的子组列表。每个子组都可以有自己的属性集。
调度权重示例¶
调度加权是为资源分配优先级的方法。调度权重较高的子组将获得更高的优先级。例如,为了确保按计划执行的管道查询的及时执行,请将其权重设置得高于即席查询。
以下是一个示例
如果您有一个根资源组 global
,有两个子组:engineering
和 marketing
。engineering
子组的调度权重为 3,marketing
子组的调度权重为 1。在此设置中,engineering
子组将获得父组 75% 的资源(因为 3 是 4 的总权重的 75%),而 marketing
子组将获得父组 25% 的资源(因为 1 是 4 的总权重的 25%)。
调度加权允许您在资源分配方面优先考虑某些子组。在此示例中,来自 engineering
子组的查询将优先于来自 marketing
子组的查询。
选择器规则¶
以下是 PrestoDB 中选择器规则的关键组成部分
group
(必需): 这些查询将在其中运行的组。user
(可选): 这是一个正则表达式,匹配提交查询的用户。source
(可选): 这匹配查询的来源,通常是提交查询的应用程序。queryType
(可选): 与提交的查询类型匹配的字符串SELECT
:SELECT
查询。EXPLAIN
:EXPLAIN
查询(但不包括EXPLAIN ANALYZE
)。DESCRIBE
:DESCRIBE
、DESCRIBE INPUT
、DESCRIBE OUTPUT
和SHOW
查询。INSERT
:INSERT
、CREATE TABLE AS
和REFRESH MATERIALIZED VIEW
查询。UPDATE
:UPDATE
查询。DELETE
:DELETE
查询。ANALYZE
:ANALYZE
查询。DATA_DEFINITION
: 更改/创建/删除架构/表/视图的元数据以及管理准备好的语句、权限、会话和事务的查询。
clientTags
(可选): 标签列表。要匹配,此列表中的每个标签都必须在与查询关联的客户端提供的标签列表中。selectorResourceEstimate
(可选): 基于资源估计的资源组选择。executionTime
peakMemory
cpuTime
clientInfo
(可选): 与客户端信息匹配的字符串。principal
(可选): 这是一个正则表达式,匹配提交查询的主体。schema
(可选): 这匹配查询的会话架构。
选择器按顺序处理,第一个匹配的选择器将被使用。
全局属性¶
cpuQuotaPeriod
(可选): 强制执行 CPU 配额的时期。cpuQuotaPeriod
是一个全局属性,通常用于基于容器的环境中,以控制容器在指定时期内可以使用的 CPU 资源量。
请注意,这些属性的具体实现和命名可能会因不同的容器运行时和编排系统而异。
提供选择器属性¶
来源名称可以按如下方式设置
CLI: 使用
--source
选项。JDBC 驱动程序在客户端应用程序中使用时:将
source
属性添加到连接配置中,并在使用使用 JDBC 驱动程序的 Java 应用程序时设置值。与 Java 程序一起使用的 JDBC 驱动程序:在
Connection
实例上添加一个键为source
、值为 示例 的属性。
客户端标签可以按如下方式设置
CLI: 使用
--client-tags
选项。JDBC 驱动程序在客户端应用程序中使用时:将
clientTags
属性添加到连接配置中,并在使用使用 JDBC 驱动程序的 Java 应用程序时设置值。使用 Java 程序的 JDBC 驱动程序:在
Connection
实例中添加一个键为clientTags
的属性,其值如 示例 所示。
示例¶
在下面的示例配置中,有多个资源组,其中一些是模板。模板允许管理员动态构建资源组树。例如,在 pipeline_${USER}
组中,${USER}
将扩展为提交查询的用户的名称。${SOURCE}
也受支持,它将扩展为提交查询的源。您也可以在 source
和 user
正则表达式中使用自定义命名变量。
有四个选择器,它们定义哪些查询在哪个资源组中运行。
第一个选择器匹配来自
bob
的查询,并将它们放置在 admin 组中。第二个选择器匹配来自包含
pipeline
的源名称的所有数据定义 (DDL) 查询,并将它们放置在global.data_definition
组中。这有助于减少此类查询的队列时间,因为它们预计会很快完成。第三个选择器匹配来自包含
pipeline
的源名称的查询,并将它们放置在global.pipeline
组下的动态创建的每个用户管道组中。第四个选择器匹配来自 BI 工具的查询,这些工具的源匹配正则表达式
jdbc#(?<toolname>.*)
,并且具有客户端提供的标记,这些标记是hi-pri
的超集。这些查询将被放置在global.pipeline.tools
组下的动态创建的子组中。动态子组是根据命名变量toolname
创建的,该变量是从源的正则表达式中提取的。考虑一个源为
jdbc#powerfulbi
、用户为kayla
且客户端标记为hipri
和fast
的查询。此查询被路由到global.pipeline.bi-powerfulbi.kayla
资源组。最后一个选择器是一个万能选择器,它将所有尚未匹配的查询放置到每个用户 adhoc 组中。
总而言之,这些选择器实现了以下策略。
用户
bob
是管理员,可以运行最多 50 个并发查询。查询将根据用户提供的优先级运行。
对于其他用户
最多可以同时运行 100 个查询。
最多可以同时运行 5 个带有源
pipeline
的并发 DDL 查询。查询按 FIFO 顺序运行。非 DDL 查询将在
global.pipeline
组下运行,总并发数为 45,每个用户并发数为 5。查询按 FIFO 顺序运行。对于 BI 工具,每个工具最多可以运行 10 个并发查询,每个用户最多可以运行 3 个。如果总需求超过 10 个的限制,则运行查询最少的用户将获得下一个并发时段。这种策略在争用情况下会导致公平性。
所有剩余的查询都将被放置到
global.adhoc.other
下的每个用户组中,其行为类似。
文件资源组管理器¶
{
"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"
}
数据库资源组管理器¶
-- global properties
INSERT INTO resource_groups_global_properties (name, value) VALUES ('cpu_quota_period', '1h');
-- Every row in resource_groups table indicates a resource group.
-- The enviroment name is 'test_environment', make sure it matches `node.environment` in your cluster.
-- The parent-child relationship is indicated by the ID in 'parent' column.
-- create a root group 'global' with NULL parent
INSERT INTO resource_groups (name, soft_memory_limit, hard_concurrency_limit, max_queued, scheduling_policy, jmx_export, environment) VALUES ('global', '80%', 100, 1000, 'weighted', true, 'test_environment');
-- get ID of 'global' group
SELECT resource_group_id FROM resource_groups WHERE name = 'global'; -- 1
-- create two new groups with 'global' as parent
INSERT INTO resource_groups (name, soft_memory_limit, hard_concurrency_limit, max_queued, scheduling_weight, environment, parent) VALUES ('data_definition', '10%', 5, 100, 1, 'test_environment', 1);
INSERT INTO resource_groups (name, soft_memory_limit, hard_concurrency_limit, max_queued, scheduling_weight, environment, parent) VALUES ('adhoc', '10%', 50, 1, 10, 'test_environment', 1);
-- get ID of 'adhoc' group
SELECT resource_group_id FROM resource_groups WHERE name = 'adhoc'; -- 3
-- create 'other' group with 'adhoc' as parent
INSERT INTO resource_groups (name, soft_memory_limit, hard_concurrency_limit, max_queued, scheduling_weight, scheduling_policy, environment, parent) VALUES ('other', '10%', 2, 1, 10, 'weighted_fair', 'test_environment', 3);
-- get ID of 'other' group
SELECT resource_group_id FROM resource_groups WHERE name = 'other'; -- 4
-- create '${USER}' group with 'other' as parent.
INSERT INTO resource_groups (name, soft_memory_limit, hard_concurrency_limit, max_queued, environment, parent) VALUES ('${USER}', '10%', 1, 100, 'test_environment', 4);
-- create 'bi-${toolname}' group with 'adhoc' as parent
INSERT INTO resource_groups (name, soft_memory_limit, hard_concurrency_limit, max_queued, scheduling_weight, scheduling_policy, environment, parent) VALUES ('bi-${toolname}', '10%', 10, 100, 10, 'weighted_fair', 'test_environment', 3);
-- create 'pipeline' group with 'global' as parent
INSERT INTO resource_groups (name, soft_memory_limit, hard_concurrency_limit, max_queued, scheduling_weight, jmx_export, environment, parent) VALUES ('pipeline', '80%', 45, 100, 1, true, 'test_environment', 1);
-- get ID of 'pipeline' group
SELECT resource_group_id FROM resource_groups WHERE name = 'pipeline'; -- 7
-- create 'pipeline_${USER}' group with 'pipeline' as parent
INSERT INTO resource_groups (name, soft_memory_limit, hard_concurrency_limit, max_queued, environment, parent) VALUES ('pipeline_${USER}', '50%', 5, 100, 'test_environment', 7);
-- create a root group 'admin' with NULL parent
INSERT INTO resource_groups (name, soft_memory_limit, hard_concurrency_limit, max_queued, scheduling_policy, environment, jmx_export) VALUES ('admin', '100%', 50, 100, 'query_priority', 'test_environment', true);
-- Selectors
-- use ID of 'admin' resource group for selector
INSERT INTO selectors (resource_group_id, user_regex, priority) VALUES ((SELECT resource_group_id FROM resource_groups WHERE name = 'admin'), 'bob', 6);
-- use ID of 'global.data_definition' resource group for selector
INSERT INTO selectors (resource_group_id, source_regex, query_type, priority) VALUES ((SELECT resource_group_id FROM resource_groups WHERE name = 'data_definition'), '.*pipeline.*', 'DATA_DEFINITION', 4);
-- use ID of 'global.pipeline.pipeline_${USER}' resource group for selector
INSERT INTO selectors (resource_group_id, source_regex, priority) VALUES ((SELECT resource_group_id FROM resource_groups WHERE name = 'pipeline_${USER}'), '.*pipeline.*', 3);
-- get ID of 'global.adhoc.other.${USER}' resource group for by disambiguating group name using parent ID
SELECT A.resource_group_id self_id, B.resource_group_id parent_id, concat(B.name, '.', A.name) name_with_parent
FROM resource_groups A JOIN resource_groups B ON A.parent = B.resource_group_id
WHERE A.name = '${USER}' AND B.name = 'other';
-- | 5 | 4 | other.${USER} |
INSERT INTO selectors (resource_group_id, priority) VALUES (5, 1);