リソースグループ

リソースグループは、リソースの使用量に制限を設け、その中で実行されるクエリに対してキューイングポリシーを強制したり、リソースをサブグループ間で分割したりできます。クエリは単一のリソースグループに属し、そのグループ(およびその上位グループ)からリソースを消費します。キューに入ったクエリの制限を除き、リソースグループがリソースを使い果たしても、実行中のクエリが失敗することはありません。代わりに、新しいクエリがキューに入れられます。リソースグループはサブグループを持つか、クエリを受け入れることができますが、両方を行うことはできません。

PrestoDBでは、リソースグループはクエリの実行とリソース割り当てを管理するための強力なツールです。これにより、管理者はPrestoクラスタでリソースがどのように割り当てられ、利用されるかを制御できます。

リソース使用量の制限

リソースグループは、CPU時間、メモリ使用量、クエリの総数など、リソース使用量に制限を設定できます。これは、単一のユーザーやクエリがシステムリソースを独占しないようにしたいマルチテナント環境で特に役立ちます。

リソース消費

クエリは単一のリソースグループに属し、そのグループだけでなく、親グループからもリソースを消費します。リソースグループが特定のリソースを使い果たしても、実行中のクエリが失敗することはありません。代わりに、リソースが再び利用可能になるまで、新しいクエリがキューに入れられます。

サブグループとクエリの受付

サブグループを使用すると、階層的なリソース割り当てが可能になり、各サブグループは独自のリソース制限とキューイングポリシーを持つことができます。クエリを直接受け入れるリソースグループはリーフグループであり、割り当てられたリソースを使用してクエリを実行します。

リソースグループと関連する選択ルールは、プラグイン可能なマネージャによって構成されます。Prestoのリソース管理は、次の2つの方法で実行できます。

ファイルベースのリソース管理

ファイルベースのリソースマネージャでは、リソースグループに関する構成情報は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. etcresource-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://localhost:3306/resource_groups?user=<user>&password=<password>

<user><password>を実際のユーザー名とパスワードに置き換えます。

データベースリソースグループマネージャを使用すると、データベースの構成に対する変更はすぐに有効になり、Prestoサーバーの再起動は必要ありません。これにより、リソースグループの構成をより柔軟に動的に変更できます。

リソースグループの構成は、resource_groups_global_propertiesresource_groups、およびselectorsのテーブルを通じて設定する必要があります。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(オプション):このグループが期間内に使用できる最大CPU時間(cpuQuotaPeriodを参照)。最大実行クエリ数にペナルティが適用される前に設定されます。hardCpuLimitも指定する必要があります。

  • schedulingPolicy(オプション):リソースグループ内でクエリがどのようにスケジュールされるかを決定するポリシー。これは、4つの値fairweightedweighted_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(オプション):リソースグループ内の各クエリが、強制終了される前に消費できる最大リソースを指定します。これらの制限は、親グループから継承されません。3種類の制限を設定できます。

    • executionTimeLimit(オプション):クエリの実行にかかる最大時間の絶対値(例:1時間)を指定します。

    • totalMemoryLimit(オプション):クエリが消費できる最大分散メモリの絶対値(例:1GB)を指定します。

    • cpuTimeLimit(オプション):クエリが使用できる最大CPU時間の絶対値(例:1時間)を指定します。

  • workerPerQueryLimit(オプション):各クエリで使用可能である必要があるワーカーの最小数を指定します。ワーカーの数が時間とともに変化する伸縮自在なクラスターで使用することを目的としています。

  • subGroups(オプション):サブグループのリスト。リソースグループ内のサブグループのリスト。各サブグループは、独自のプロパティセットを持つことができます。

スケジューリングの重みの例

スケジューリングの重み付けは、リソースに優先度を割り当てる方法です。スケジューリングの重みが大きいサブグループには、より高い優先順位が与えられます。たとえば、スケジュールされたパイプラインクエリのタイムリーな実行を保証するには、アドホッククエリよりも重み付けを高くします。

次に例を示します。

ルートリソースグループglobalに、2つのサブグループ(engineeringmarketing)があるとします。engineeringサブグループのスケジューリングの重みは3で、marketingサブグループのスケジューリングの重みは1です。この設定では、engineeringサブグループは親グループのリソースの75%(3は合計重み4の75%であるため)を取得し、marketingサブグループは親グループのリソースの25%(1は合計重み4の25%であるため)を取得します。

スケジューリングの重み付けを使用すると、リソース割り当ての観点から特定のサブグループを他のサブグループよりも優先することができます。この例では、engineeringサブグループからのクエリは、marketingサブグループからのクエリよりも優先されます。

セレクタールール

PrestoDBのセレクタールールの主要コンポーネントは次のとおりです。

  • group(必須):これらのクエリが実行されるグループ。

  • user(オプション):これは、クエリを送信しているユーザーに一致する正規表現です。

  • source(オプション):これは、クエリのソース(通常はクエリを送信しているアプリケーション)と一致します。

  • queryType(オプション):送信されたクエリのタイプと照合する文字列

    • SELECTSELECTクエリ。

    • EXPLAINEXPLAINクエリ(ただし、EXPLAIN ANALYZEは除く)。

    • DESCRIBEDESCRIBEDESCRIBE INPUTDESCRIBE OUTPUT、およびSHOWクエリ。

    • INSERTINSERTCREATE TABLE AS、およびREFRESH MATERIALIZED VIEWクエリ。

    • UPDATEUPDATEクエリ。

    • DELETEDELETEクエリ。

    • ANALYZEANALYZEクエリ。

    • DATA_DEFINITION:スキーマ/テーブル/ビューのメタデータを変更/作成/削除し、プリペアドステートメント、権限、セッション、トランザクションを管理するクエリ。

  • clientTags(オプション):タグのリスト。一致させるには、このリスト内のすべてのタグが、クエリに関連付けられているクライアント提供のタグのリストに含まれている必要があります。

  • selectorResourceEstimate(オプション):リソース推定に基づくリソースグループの選択。
    • executionTime

    • peakMemory

    • cpuTime

  • clientInfo(オプション):クライアント情報と照合する文字列。

  • principal(オプション):これは、クエリを送信しているプリンシパルに一致する正規表現です。

  • schema(オプション):これは、クエリのセッションスキーマと一致します。

セレクターは順番に処理され、最初に一致したものが使用されます。

グローバルプロパティ

  • cpuQuotaPeriod(オプション):CPUクォータが適用される期間。cpuQuotaPeriodは、指定された期間内にコンテナが使用できるCPUリソースの量を制御するために、コンテナベースの環境でよく使用されるグローバルプロパティです。

これらのプロパティの正確な実装と名前は、コンテナランタイムとオーケストレーションシステムによって異なる場合があることに注意してください。

セレクタープロパティの提供

ソース名は次のように設定できます。

  • CLI:--sourceオプションを使用します。

  • クライアントアプリで使用する場合のJDBCドライバー:JavaアプリケーションでJDBCドライバーを使用する場合、接続構成にsourceプロパティを追加して値を設定します。

  • Javaプログラムで使用するJDBCドライバー:sourceキーと値をConnectionインスタンスに追加します。を参照してください。

クライアントタグは次のように設定できます。

  • CLI: --client-tagsオプションを使用します。

  • クライアントアプリで使用する場合のJDBCドライバー:JDBCドライバーを使用するJavaアプリケーションを使用する場合は、接続構成にclientTagsプロパティを追加し、値を設定します。

  • Javaプログラムで使用するJDBCドライバー:clientTagsキーと値をConnectionインスタンスに追加します。を参照してください。

以下の設定例では、いくつかのリソースグループがあり、そのうちのいくつかはテンプレートです。テンプレートを使用すると、管理者はリソースグループツリーを動的に構築できます。たとえば、pipeline_${USER}グループでは、${USER}はクエリを送信したユーザーの名前に展開されます。${SOURCE}もサポートされており、クエリを送信したソースに展開されます。また、sourceおよびuser正規表現で、カスタムの名前付き変数を使用することもできます。

どのクエリをどのリソースグループで実行するかを定義する4つのセレクターがあります。

  • 最初のセレクターはbobからのクエリを照合し、それらをadminグループに配置します。

  • 2番目のセレクターは、pipelineを含むソース名からのすべてのデータ定義(DDL)クエリを照合し、それらをglobal.data_definitionグループに配置します。これにより、このクラスのクエリは高速であると予想されるため、キューの時間を短縮できます。

  • 3番目のセレクターは、pipelineを含むソース名からのクエリを照合し、global.pipelineグループの下に動的に作成されたユーザーごとのパイプライングループに配置します。

  • 4番目のセレクターは、正規表現jdbc#(?<toolname>.*)に一致するソースを持ち、hi-priのスーパーセットであるクライアント提供のタグを持つBIツールからのクエリを照合します。これらは、global.pipeline.toolsグループの下に動的に作成されたサブグループに配置されます。動的なサブグループは、ソースの正規表現から抽出される名前付き変数toolnameに基づいて作成されます。

    ソースがjdbc#powerfulbi、ユーザーがkayla、クライアントタグがhiprifastであるクエリを考えてみましょう。このクエリは、global.pipeline.bi-powerfulbi.kaylaリソースグループにルーティングされます。

  • 最後のセレクターは、まだ一致していないすべてのクエリをユーザーごとのアドホックグループに配置するキャッチオールです。

これらのセレクターは、連携して次のポリシーを実装します。

  • ユーザーbobは管理者であり、最大50個の同時クエリを実行できます。クエリは、ユーザーが提供する優先度に基づいて実行されます。

残りのユーザーの場合

  • 合計で100個を超えるクエリを同時に実行することはできません。

  • ソースがpipelineである最大5つの同時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);