資源群組

資源群組限制資源使用量,並可以對在其中執行的查詢強制執行排隊原則,或在其子群組之間分配資源。一個查詢屬於單一資源群組,並從該群組(及其上層群組)消耗資源。除了對排隊查詢的限制之外,當資源群組耗盡資源時,不會導致正在執行的查詢失敗;而是新的查詢會進入排隊狀態。一個資源群組可以有子群組或可以接受查詢,但不能同時執行這兩者。

在 PrestoDB 中,資源群組是管理查詢執行和資源分配的強大工具。它們允許管理員控制如何在 Presto 叢集上分配和使用資源。

資源使用限制

資源群組可以設定資源使用量的限制,例如 CPU 時間、記憶體使用量或查詢總數。這在多租戶環境中特別有用,在這種環境中,您希望確保沒有單一使用者或查詢獨佔系統資源。

資源消耗

一個查詢屬於單一資源群組,並且它從該群組及其父群組消耗資源。如果資源群組耗盡某種資源,則不會導致正在執行的查詢失敗。相反,新的查詢將會排隊,直到資源再次可用。

子群組和查詢接受

子群組允許階層式資源分配,其中每個子群組都可以有自己的資源限制和排隊原則。直接接受查詢的資源群組是葉群組,它使用其分配的資源執行查詢。

資源群組和相關的選擇規則由一個可插入的管理器配置。Presto 資源管理可以透過兩種方式完成

基於檔案的資源管理

在基於檔案的資源管理器中,關於資源群組的組態資訊儲存在 JSON 檔案中。此檔案包含所有資源群組的定義和選取它們的規則。組態檔案會在 Presto 伺服器啟動時載入並使用。對檔案的任何變更都需要重新啟動 Presto 伺服器才能生效。

基於資料庫的資源管理

在基於資料庫的資源管理器中,關於資源群組的組態資訊儲存在關聯式資料庫中。資料庫包含所有資源群組的定義和選取它們的規則。與基於檔案的資源管理器不同,對資料庫中組態的變更會立即生效,不需要重新啟動 Presto 伺服器。

兩種方法各有優缺點。基於檔案的資源管理設定較簡單,但彈性較差,而基於資料庫的資源管理設定較複雜,但提供較大的彈性和動態變更。

檔案資源群組管理器

PrestoDB 中的檔案資源群組管理器是使用 JSON 組態檔案管理資源的一種方式。此檔案包含所有資源群組的定義,以及針對給定查詢選取適當資源群組的規則。

要設定基於檔案的資源群組管理器

  1. 建立檔案 etc/resource-groups.properties

  2. etc/resource-groups.properties 中,將 resource-groups.configuration-manager 屬性設定為 file,使用以下程式碼範例

    resource-groups.configuration-manager=file
    
  3. etc 中建立名為 resource-groups.json 的 JSON 檔案。此檔案應包含資源群組的定義。每個資源群組可以指定最大記憶體、最大排隊查詢和最大執行查詢等項目。

    如需建立資源群組定義的相關資訊,請參閱 資源群組屬性。如需 resource-groups.json 檔案的範例,請參閱 檔案資源群組管理器

  4. etc/resource-groups.properties 中,加入一行,使用以下程式碼範例指定 JSON 檔案的位置。將 resource-groups.config-file 屬性設定為 <file_path>,其中 <file_path> 是 JSON 檔案的路徑。

    resource-groups.config-file=etc/resource-groups.json
    
  5. 重新啟動 Presto 伺服器。新的資源群組會在重新啟動後立即生效。

資料庫資源群組管理器

PrestoDB 中的資料庫資源群組管理器是使用關聯式資料庫管理資源的一種方式。

要設定基於資料庫的資源群組管理器

  1. 建立檔案 etc/resource-groups.properties

  2. resource-groups.configuration-manager 屬性設定為 db

  3. 設定關聯式資料庫:Presto 應可存取此資料庫。它將用於儲存資源群組的組態。

  4. 為資源群組和選取規則建立表格:您需要在資料庫中建立表格,以儲存資源群組的定義和針對給定查詢選取適當資源群組的規則。

  5. 若要指定 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_propertiesresource_groupsselectors 填入。如果在 Presto 啟動時,任何表格不存在,則會自動建立這些表格。

selectors 表格中的規則會按照 priority 欄位中值的降序順序處理。

資料庫資源群組管理器屬性

資料庫資源群組管理器屬性

屬性名稱

描述

預設值

resource-groups.config-db-url

從中載入組態的資料庫 URL。

resource-groups.max-refresh-interval

叢集在重新整理失敗後,將會繼續接受查詢的最長時間段,導致組態過時。

1 小時

resource-groups.exact-match-selector-enabled

設定此旗標可啟用額外的 exact_match_source_selectors 表格的使用,以針對來源、環境和查詢類型,組態定義基於確切名稱比對的資源群組選取規則。依預設,規則僅從 selectors 表格載入,其中包含針對 source 的正規表示式篩選器,以及其他篩選器。

false

資源群組屬性

資源群組使用一組屬性來定義,這些屬性決定如何分配和使用資源。以下是可以為資源群組設定的關鍵屬性

  • name (必要): 資源群組的名稱。這是一個強制屬性。

  • maxQueued (必要): 資源群組中可以排隊的最大查詢數量。如果達到此限制,新的查詢將會被拒絕。

  • hardCpuLimit (選用): 此群組在一段時間內可以使用的最大 CPU 時間量。

  • softMemoryLimit (必要): 資源群組可以使用的最大記憶體量。這可以用絕對值(如「10GB」)或可用總記憶體的百分比(如「50%」)來指定。

  • hardConcurrencyLimit (必要): 資源群組可以同時執行的最大查詢數量。

  • softConcurrencyLimit (選用): 同時查詢數量的軟性限制。如果超過此限制,排程器將嘗試阻止新的查詢開始,但不會強制停止正在執行的查詢。

  • softCpuLimit (選用): 此群組在一段時間內(請參閱 cpuQuotaPeriod)可以使用的最大 CPU 時間量,之後會對最大執行查詢數量施加懲罰。也必須指定 hardCpuLimit

  • schedulingPolicy (選用): 決定如何在資源群組中排程查詢的策略。可以設定為四個值之一:fairweightedweighted_fairquery_priority

    • fair (預設): 排隊的查詢會按照先進先出 (FIFO) 的順序處理,如果子群組有任何排隊的查詢,則子群組必須輪流開始新的查詢。

    • 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,其中有兩個子群組:engineeringmarketingengineering 子群組的排程權重為 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: DESCRIBEDESCRIBE INPUTDESCRIBE OUTPUTSHOW 查詢。

    • INSERT: INSERTCREATE TABLE ASREFRESH 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} 也被支援,它會展開為提交查詢的來源。您也可以在 sourceuser 正規表示式中使用自訂命名變數。

有四個選擇器,定義哪些查詢會在哪個資源群組中執行。

  • 第一個選擇器比對來自 bob 的查詢,並將它們放置在管理員群組中。

  • 第二個選擇器比對來自包含 pipeline 的來源名稱的所有資料定義 (DDL) 查詢,並將它們放置在 global.data_definition 群組中。這有助於減少這類查詢的佇列時間,因為它們預期會很快執行。

  • 第三個選擇器比對來自包含 pipeline 的來源名稱的查詢,並將它們放置在 global.pipeline 群組下動態建立的每個使用者管道群組中。

  • 第四個選擇器比對來自 BI 工具的查詢,這些工具的來源符合正規表示式 jdbc#(?<toolname>.*),且具有客戶端提供的標籤,這些標籤是 hi-pri 的超集。這些查詢會被放置在 global.pipeline.tools 群組下動態建立的子群組中。動態子群組是基於命名變數 toolname 建立的,該變數是從來源的正規表示式中提取的。

    考慮一個來源為 jdbc#powerfulbi、使用者為 kayla 且客戶端標籤為 hiprifast 的查詢。此查詢會被路由到 global.pipeline.bi-powerfulbi.kayla 資源群組。

  • 最後一個選擇器是捕捉所有未比對到的查詢,並將它們放置在每個使用者的臨時群組中。

總之,這些選擇器實作了以下策略:

  • 使用者 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);