リソース
- 時間共有リソース (CPU, I/O, クエリスロット) - スケジューリング階層の葉ノードでエンキューされるリソース要求を管理します。要求は、階層で定義されたポリシーと制約に従ってスケジュールされます。リソース要求は、クエリが対応するリソースにアクセスしたときに作成されます。たとえば、クエリがディスクからデータを読み取るときや、処理のために CPU を使用するときには、処理量子ごと、またはソケット経由で送受信されたバイト数ごとにリソース要求が作成されます。
- 空間共有リソース (メモリ) - スケジューリング階層の葉ノードにおけるリソース割り当てを管理します。割り当ては、実行中または保留中のいずれかです。保留中の割り当ては、十分な空き容量が確保されるか、ほかの割り当てが追い出される (強制終了される) までブロックされます。判断は、階層で定義された制限とポリシーに基づいて行われます。割り当てとクエリ (またはバックグラウンドアクティビティ) は 1 対 1 で対応します。割り当ては、クエリの実行開始時に作成され、終了時に解放されます。実行中の割り当ては、そのサイズを動的に増減できます。
ワークロード階層
max_* 設定で定義された上限は host ごとに適用されます。ワークロード “user” は、そのリソースを “development” ワークロードと “production” ワークロードの間でさらに分割し、“production” は “development” の 3 倍のリソースを持ちます:
SETTINGS workload = 'name' で使用できます。詳細については、Workload markupを参照してください。
ワークロードをカスタマイズするには、次の設定を使用できます。
priority- (time-shared のみ) 兄弟ワークロードは静的な値に従って処理されます (値が小さいほど優先度が高くなります) 。プリエンプションに影響します。precedence- (space-shared のみ) 兄弟ワークロードは静的な値に従って受け入れられます (値が小さいほど優先順位 が高くなります) 。エビクションと admission に影響します。weight- 同じ静的 priority または 優先順位 を持つ兄弟ワークロードは、重みに応じて公平にリソースを共有します。プリエンプション、エビクション、および admission に影響します。max_io_requests- このワークロードにおける同時実行 I/O リクエスト数の上限です。max_bytes_inflight- このワークロードにおける同時実行リクエストの処理中バイト総量の上限です。max_bytes_per_second- このワークロードの読み取りまたは書き込みのバイトレートの上限です。max_burst_bytes- スロットリングされることなくこのワークロードで処理できる最大バイト数です (リソースごとに個別に適用されます) 。max_concurrent_threads- このワークロード内のクエリに対するスレッド数の上限です。max_concurrent_threads_ratio_to_cores-max_concurrent_threadsと同じですが、使用可能な CPU コア 数に対して正規化されます。max_cpus- このワークロード内のクエリを処理する CPU コア 数の上限です。max_cpu_share-max_cpusと同じですが、使用可能な CPU コア 数に対して正規化されます。max_burst_cpu_seconds-max_cpusによってスロットリングされることなく、このワークロードが消費できる最大 CPU 秒数です。max_memory- このワークロード用に予約されるメモリ総量の上限です。
max_bytes_per_second = '10Mi' を持つワークロードには、各読み取りリソースおよび各書き込みリソースに対して、それぞれ独立した 10 MB/s の帯域幅上限が適用されます。読み取りと書き込みに共通の上限が必要な場合は、READ アクセスと WRITE アクセスに同じリソースを使用することを検討してください。
リソースごとに異なるワークロード階層を指定する方法はありません。ただし、特定のリソースに対して異なるワークロード設定値を指定する方法はあります。
CREATE OR REPLACE WORKLOADクエリを使用します。
ワークロード設定は、適切なスケジューリングノードのセットに変換されます。より下位レベルの詳細については、スケジューリングノードの種類とオプションの説明を参照してください。
ワークロードの指定
workload を指定できます。workload が設定されていない場合は、値 “default” が使用されます。なお、設定プロファイルを使用して別の値を指定することもできます。ユーザーからのすべてのクエリに workload 設定の固定値を付与したい場合は、設定の制約を使用して workload を定数にできます。
workload 設定を割り当てることも可能です。マージとミューテーションでは、それぞれ merge_workload および mutation_workload サーバー設定が使用されます。これらの値は、merge_workload および mutation_workload MergeTree 設定を使用して、特定のテーブルごとに上書きすることもできます。
CPU スケジューリング
- マスタースレッド — クエリ、または merge や mutation のようなバックグラウンド処理で最初に動作を開始するスレッド。
- ワーカースレッド — CPU 負荷の高いタスクを処理するために、マスターが追加で生成できるスレッド。
max_threads クエリ設定の値が大きいと、多数のワーカースレッドが CPU リソースを容易に占有してしまいます。すると、新たに到着したクエリは、マスタースレッドが実行を開始するための CPU スロット が空くまでブロックされて待機することになります。これを避けるには、次の設定を使用できます。
cpu_slot_preemption サーバー設定で有効にします。これが有効な場合、各スレッドは CPU スロットを定期的に更新します (cpu_slot_quantum_ns サーバー設定に従います) 。この更新によって、CPU が過負荷のときは実行がブロックされることがあります。実行が長時間ブロックされると (cpu_slot_preemption_timeout_ms サーバー設定を参照) 、クエリはスケールダウンし、同時実行中のスレッド数が動的に減少します。CPU 時間の公平性はワークロード間では保証されますが、同じワークロード内のクエリ間では、一部の特殊なケースで損なわれる可能性があることに注意してください。
CPU リソースを宣言すると、
concurrent_threads_soft_limit_num および concurrent_threads_soft_limit_ratio_to_cores 設定は効かなくなります。代わりに、特定のワークロードに割り当てる CPU 数の制限には、ワークロード設定 max_concurrent_threads が使用されます。従来の動作を再現するには、WORKER THREAD リソースのみを作成し、ワークロード all の max_concurrent_threads を concurrent_threads_soft_limit_num と同じ値に設定したうえで、クエリ設定 workload = "all" を使用してください。この構成は、concurrent_threads_scheduler 設定を “fair_round_robin” にした場合に相当します。スレッドと CPU
- スレッド数の制限:
max_concurrent_threadsとmax_concurrent_threads_ratio_to_cores - CPU スロットリング:
max_cpus、max_cpu_share、max_burst_cpu_seconds
max_threads で指定される値を引き下げます。2 つ目は、トークンバケットアルゴリズムを使ってワークロードの CPU 消費をスロットリングします。これはスレッド数自体には直接影響しませんが、ワークロード内のすべてのスレッドによる CPU の総消費量をスロットリングします。
max_cpus と max_burst_cpu_seconds によるトークンバケットスロットリングは、次のことを意味します。任意の delta 秒の期間において、ワークロード内のすべてのクエリによる CPU 総消費量は max_cpus * delta + max_burst_cpu_seconds CPU 秒を超えてはなりません。長期的には平均消費量が max_cpus によって制限されますが、短期的にはこの上限を超える場合があります。たとえば、max_burst_cpu_seconds = 60 かつ max_cpus=0.001 の場合、スロットリングされることなく、1 スレッドを 60 秒間、2 スレッドを 30 秒間、または 60 スレッドを 1 秒間実行できます。max_burst_cpu_seconds のデフォルト値は 1 秒です。値を小さくしすぎると、同時実行スレッドが多い場合に、許可された max_cpus コアを十分に使い切れないことがあります。
CPU スロットを保持している間、スレッドは次の 3 つの主要な状態のいずれかになります。
- Running: 実際に CPU リソースを消費している状態。この状態で費やされた時間は CPU スロットリングの対象として計上されます。
- Ready: CPU が利用可能になるのを待っている状態。この状態で費やされた時間は CPU スロットリングの対象にはなりません。
- Blocked: I/O 操作やその他のブロッキング syscall (例: mutex の待機) を行っている状態。この状態で費やされた時間は CPU スロットリングの対象にはなりません。
max_cpu_share に基づく上限は CPU リソース全体の 70% です。一方、インジェストには少なくとも 0.8 * 0.25 = 20% が保証され、上限はありません。
ClickHouse server で CPU 使用率を最大化したい場合は、ルート ワークロード
all に対して max_cpus や max_cpu_share を使用しないでください。代わりに、max_concurrent_threads により大きな値を設定してください。たとえば、8 CPU のシステムでは、max_concurrent_threads = 16 に設定します。これにより、8 スレッドが CPU タスクを実行している間に、別の 8 スレッドで I/O 操作を処理できます。追加のスレッドによって CPU 負荷が生じるため、スケジューリングルールが確実に適用されます。これに対して、max_cpus = 8 を設定しても CPU 負荷は発生しません。これは、server が利用可能な 8 CPU を超えて使用できないためです。メモリ予約
メモリ予約のスケジューリングは実験的な機能です。これは
MEMORY RESERVATION リソースが存在する場合にのみ有効であり、SQL インターフェースおよび動作は今後のリリースで変更される可能性があります。現時点では、マージとミューテーションにはまだ対応しておらず、実行中のクエリのエビクションはベストエフォートです。つまり、即時に反映されるのではなく、そのクエリの次回のメモリ同期ポイントで有効になります。MEMORY RESERVATION リソースを作成し、ワークロード設定 を使用して予約される総メモリの上限を少なくとも 1 つ設定します。
reserve_memory 設定が 0 より大きい場合、その割り当ては pending 状態で作成されます。pending の割り当ては、ワークロード階層内で要求された量のメモリを予約します。使用可能なメモリが不足している場合、その割り当ては、十分なメモリが解放されるか、ほかの割り当てが追い出される (強制終了される) まで pending のままです。割り当てが受理されると、running になります。running の割り当ては、クエリのメモリ使用量に応じてサイズを動的に増減できます。割り当てのライフサイクルは、次の状態図で表せます。
リーフワークロードの保留中の割り当ては、FIFO 順で受け入れられます。複数のワークロードに保留中の割り当てがある場合は、優先順位 と weight の設定に従って受け入れられます。優先順位 が高いワークロードほど先に処理されます。同じ 優先順位 を持つ兄弟ワークロードは、max-min fair な方式で weight に応じてメモリを共有します。つまり、正規化されたメモリ使用量 (現在の使用量に要求された増加分を加え、それを weight で割った値) が小さいワークロードほど優先して処理されます。エビクション時には逆のロジックが適用されます。メモリを解放する必要がある場合は、優先順位 が低く、正規化されたメモリ使用量が大きいワークロードから先に追い出されます。
時間共有リソースでは priority を使用し、空間共有リソースでは 優先順位 を使用する点に注意してください。これらは独立した設定であり、異なる値を設定できます。priority が高い場合は非破壊的なプリエンプション (遅延またはスロットリング) を意味し、優先順位 が高い場合は破壊的なエビクション (エラーで停止) を伴う可能性があります。たとえば、あるワークロードでは CPU スケジューリングのために高い priority を設定しつつ、メモリ予約については他のワークロードを追い出して、それまでに完了した作業を失わせないよう、同じ 優先順位 にしておくことができます。
max_memory 制限を持つすべてのワークロードでは、そのサブツリー内で割り当てられるメモリの合計が制限を超えないことが保証されます。保留中または増加中の割り当てによって制限を超える場合は、メモリを解放するためにエビクション手順が開始されます。エビクション手順では、kill する対象が選択されます。killer と対象の最小共通祖先ワークロードは、次の状況ではエビクションを防ぎます。
- 保留中の割り当ては、同じワークロード内で実行中の割り当てを追い出すことはできません。 (killer と対象のワークロードが一致するため) 。
- 優先順位 が低い保留中の割り当てが、優先順位 が高いワークロードを kill することはありません。
- 保留中の割り当ては、同じ 優先順位 の割り当てを kill できません。同じ 優先順位 の実行中の割り当て同士は、正規化されたメモリ使用量に基づいて互いを追い出す可能性がある点に注意してください。 エビクションが防がれるか、十分なメモリを解放できない場合、新しい割り当ては十分なメモリが解放されるまでブロックされます。これらのルールにより、メモリ負荷に応じて過剰なクエリをキューに入れられるようになり、MEMORY_LIMIT_EXCEEDED エラーを回避する便利な方法が提供されます。
ワークロードの制限は、max_memory_usage クエリ設定のような、メモリ消費量を制限するほかの方法とは独立しています。これらを組み合わせて使用することで、メモリ消費量をより適切に制御できます。ユーザー単位 (ワークロード単位ではなく) で個別のメモリ制限を設定することも可能です。ただし、こちらは柔軟性が低く、メモリ予約や保留中クエリのキューイングといった機能は提供しません。メモリオーバーコミット を参照してください
max_waiting_queries は、そのワークロードで保留できる割り当て数を制限します。制限に達すると、server はエラー SERVER_OVERLOADED を返します。max_waiting_queries は子ワークロードには継承されず、リーフワークロードでのみ意味を持つ点に注意してください。
メモリ予約スケジューリングは、merges と mutations ではまだサポートされていません。
reserve_memory 設定が 0 より大きいクエリのみが、メモリ予約の完了を待つ間にブロックされる対象になります。ただし、reserve_memory が 0 のクエリも ワークロード のメモリ使用量には計上され、他の保留中または増加中の割り当てのためにメモリを解放する必要がある場合は、追い出される可能性があります。ワークロード の適切なマークアップがないクエリは、メモリ予約のスケジューリングの対象にならず、スケジューラによって追い出されることもありません。
クエリに対して非弾力的なメモリ予約を行うには、reserve_memory と max_memory_usage の両方のクエリ設定を同じ値に設定します。この場合、そのクエリは一定量のメモリを固定で予約し、割り当てを動的に増やすことはできません。なお、弾力的なメモリ予約は、メモリ逼迫がない限り停止されることなく、reserve_memory を超えて max_memory_usage まで増やせます。ただし、実際の使用量がそれより少なくても、reserve_memory 未満に減らすことはできません。
設定例を見てみましょう。
クエリスロットのスケジューリング
max_concurrent_queries は、特定のワークロードで同時に実行できるクエリ数を制限します。これは、クエリ設定 max_concurrent_queries_for_all_users およびサーバー設定 max_concurrent_queries に相当します。非同期 INSERT クエリと、KILL のような一部の特殊なクエリは、この上限にはカウントされません。
ワークロード設定 max_queries_per_second と max_burst_queries は、トークンバケットスロットラーを使用して、そのワークロードのクエリ数を制限します。これにより、任意の時間間隔 T において、実行を開始する新規クエリ数が max_queries_per_second * T + max_burst_queries を超えないことが保証されます。
ワークロード設定 max_waiting_queries は、そのワークロードの待機中クエリ数を制限します。上限に達すると、サーバーはエラー SERVER_OVERLOADED を返します。max_waiting_queries は子ワークロードには継承されず、リーフワークロードでのみ意味を持つことに注意してください。
ブロックされたクエリは、すべての制約が満たされるまで無期限に待機し、
SHOW PROCESSLIST には表示されません。ワークロードとリソースの保存
CREATE WORKLOAD および CREATE RESOURCE クエリ形式の、すべてのワークロードとリソースの定義は、workload_path のディスク上、または workload_zookeeper_path の ZooKeeper に永続的に保存されます。ノード間の整合性を確保するには、ZooKeeper ストレージの使用を推奨します。あるいは、ディスクストレージとあわせて ON CLUSTER 句を使用することもできます。
設定ベースのワークロードとリソース
Configuration のフォーマット
CREATE WORKLOAD 文および CREATE RESOURCE 文と同じ SQL 構文を使用します。すべてのクエリは有効である必要があります。
使用時の推奨事項
- インフラストラクチャの制限を設定するため、設定内でルートワークロードとネットワーク I/O リソースを定義します
- これらの制限を確実に適用するため、
throw_on_unknown_workloadを設定します - すべてのクエリに制限を自動適用するため、
CREATE WORKLOAD default IN allを作成します (workloadクエリ設定のデフォルト値は ‘default’ であるため) - 設定された階層内で、ユーザーが追加のワークロードを作成できるようにします
厳格なリソースアクセス
throw_on_unknown_workload があります。これを true に設定すると、すべてのクエリで有効な workload クエリ設定の指定が必須になり、指定しない場合は RESOURCE_ACCESS_DENIED 例外が発生します。これを false に設定すると、そのようなクエリはリソーススケジューラを使用せず、つまり任意の RESOURCE に無制限にアクセスできます。クエリ設定 use_concurrency_control = 0 を指定すると、クエリは CPU スケジューラを回避し、CPU を無制限に使用できます。CPU スケジューリングを強制するには、use_concurrency_control を読み取り専用の定数値として維持する設定制約を作成してください。
CREATE WORKLOAD default を実行していない限り、throw_on_unknown_workload を true に設定しないでください。起動中に workload が明示的に設定されていないクエリが実行されると、サーバーの起動時に問題が発生する可能性があります。スケジューリングノードの階層
inflight_limit(constraint) - 処理中の同時実行リクエスト数がmax_requestsを超えるか、それらの合計コストがmax_costを超える場合にブロックします。子ノードは 1 つだけである必要があります。bandwidth_limit(constraint) - 現在の帯域幅がmax_speedを超える場合 (0 は無制限を意味します) 、またはバーストがmax_burstを超える場合 (デフォルトではmax_speedに等しい) にブロックします。子ノードは 1 つだけである必要があります。fair(policy) - max-min 公平性に基づいて、子ノードのいずれかから次に処理するリクエストを選択します。子ノードではweightを指定できます (デフォルトは 1) 。priority(policy) - 静的な優先度に基づいて、子ノードのいずれかから次に処理するリクエストを選択します (値が小さいほど優先度が高くなります) 。子ノードではpriorityを指定する必要があります (デフォルトは 0) 。fifo(queue) - リソース容量を超えるリクエストを保持できる階層のリーフです。
limit- 子ノードの合計割り当てが上限を超えないようにし、必要に応じてサブツリーでエビクション手順を開始します。子ノードは 1 つだけである必要があります。fair_allocation- max-min 公平性に基づいてエビクションを実施します。保留中の割り当てが実行中の割り当てをエビクションすることはありません。子ノードではweightを指定できます (デフォルトは 1) 。precedence_allocation- 静的な優先順位に基づいてエビクションを実施します (値が小さいほど優先順位が高くなります) 。優先順位の高い保留中の割り当ては、優先順位の低い割り当てをエビクションします。子ノードではprecedenceを指定する必要があります (デフォルトは 0) 。queue- 実行中および保留中の割り当てを保持できる階層のリーフです。
非推奨の XML 設定
storage_configuration があります。
特定のディスクで I/O スケジューリングを有効にするには、ストレージ構成で read_resource および/または write_resource を指定する必要があります。これにより ClickHouse に、そのディスクに対する各読み取りリクエストおよび書き込みリクエストでどのリソースを使うかを指定できます。読み取りリソースと書き込みリソースは同じリソース名を参照でき、これはローカルSSD や HDD で有用です。複数の異なるディスクが同じリソースを参照することもでき、これはリモートディスクで有用です。たとえば、"production" ワークロードと "development" ワークロードの間でネットワーク帯域幅を公平に分配できるようにしたい場合に役立ちます。
例:
関連項目
- system.scheduler
- system.workloads
- system.resources
- merge_workload MergeTree 設定
- merge_workload グローバルなサーバー設定
- mutation_workload MergeTree 設定
- mutation_workload グローバルなサーバー設定
- workload_path グローバルなサーバー設定
- workload_zookeeper_path グローバルなサーバー設定
- cpu_slot_preemption グローバルなサーバー設定
- cpu_slot_quantum_ns グローバルなサーバー設定
- cpu_slot_preemption_timeout_ms グローバルなサーバー設定