TiDBスケジューリング

スケジューリングを行う理由

前回は、TiKVクラスタがTiDBデータベースの分散KVストレージエンジンであることを紹介した。データはRegionに複製された後に管理される。各Regionには、異なるTiKVノードに分散された複数のレプリカがある。これらのレプリカのうちリーダーは読み書きを担当し、フォロワーはリーダーから送られたRaftログを同期する。では、次の質問について考えてみよう。

  • 同じRegionの複数レプリカが異なるノードに分散されていることを、どのように確認すればよいか。また、1台のコンピュータで複数のTiKVインスタンスを起動する場合はどうするか。
  • TiKVクラスタが災害復旧に備えて複数の場所にデプロイされている場合、単一データセンターに障害が発生しても、Raftグループの複数レプリカが失われないようにするにはどうすればよいか。
  • TiKVクラスタの他ノードのデータを新しく追加されたノードへ移動するにはどうすればよいか。
  • ノードが故障したらどうするか。クラスタ全体では何をすべきか。ノードに一時的な障害、たとえばサービス再起動が発生した場合、どう解決するか。長時間の障害、たとえばディスク障害やデータ損失の場合はどうするか。
  • 各RaftグループにはN個のレプリカが必要である。1つのRaftグループでレプリカが不足する場合、たとえばノードが失敗してレプリカが失われた場合や、多すぎる場合、たとえば障害ノードが再実行され自動的にクラスタへ追加された場合がある。どのようにレプリカ数を設定できるか。読み書きはリーダーが行うが、すべてのリーダーが複数ノードに集まった場合、クラスタはどうなるか。
  • すべてのRegionへアクセスする必要はなく、ホットスポットが複数Regionに存在するように見える。この場合はどうするか。
  • クラスタはロードバランシング中にデータをマイグレーションしなければならない。このデータ移動はネットワーク帯域幅、ディスクI/O、CPUを多く消費するが、オンラインサービスに影響しないか。

上記の問題を一つずつ解決するのは簡単だが、組み合わせると難しくなる。たとえば、レプリカを追加するかどうかは、数が不足しているかどうかを確認し、1つのRaftグループの内部状況を考えればよい問題もある。しかし実際には、このレプリカをどこに追加するかという包括的な観点が必要である。システム全体は動的に変化している。Region分割、ノード参加、ノード故障、ホットスポットアクセス変更などが常に発生する。スケジューリングシステムもまた、最良の状態を維持するために継続して調整されなければならない。全体情報を把握し、スケジューリングし、構成できるコンポーネントがなければ、これらの要求を満たすのは難しい。したがって、システム全体の状況を制御し調整する中央ノードが必要である。そこで登場するのがPlacement Driver(PD)モジュールである。

スケジューリング要件

前述の質問を分類して整理してみる。大きく分けて二種類がある。

分散型で高可用なストレージシステムは、次の要件を満たさなければならない。

  • 適切な数のレプリカ作成
  • レプリカを別コンピュータに分散
  • ノード追加後、別ノードのレプリカを移動可能にする
  • ノードがオフラインのとき、ノードのデータを移動する

優れた分散システムには次の最適化が必要である。

  • クラスタでリーダーをバランスよく配置する
  • 各ノードのストレージ容量をバランスよく配置する
  • ホットスポットアクセスをバランスよく分散する
  • オンラインサービスに影響しないバランシング速度制御
  • 手動でノードのオンライン/オフラインを切り替える、または障害ノードを自動的にオフラインにするなどのノード状態管理

最初の要件を満たす場合、システムは複数レプリカによる災害復旧、動的拡張性、ノード障害耐性、自動災害復旧をサポートする。そして二つ目の要件を満たすと、システムの負荷分散がよく、管理しやすくなる。

これらの要件を満たすには、まず各ノードの状態、各Raftグループの情報、ビジネスアクセス、運用統計などの情報を十分に収集しなければならない。その後、PDがこれらの情報とスケジューリングポリシーに基づいて上記要求を満たすスケジューリング計画を立てるよう、ポリシーを設定する必要がある。

スケジューリングの基本操作

スケジューリングの基本操作は非常に単純である。つまり、スケジューリングポリシーに従えるということである。これがスケジューラ全体の基盤である。

上記のスケジューラ要件は複雑に見えるが、大きく三つの操作に分けられる。

  • レプリカ追加
  • レプリカ削除
  • Raftグループの別レプリカ間でリーダー役割を移動

Raftプロトコルは偶然にもこれらの要件を満たし、AddReplicaRemoveReplicaTransferLeaderの各コマンドはこの三つの基本操作をサポートする。

情報収集

スケジューリングはクラスタ全体の情報収集に依存する。簡単に言えば、各TiKVノードと各Regionの状態を知る必要がある。TiKVクラスタは二種類の情報をPDへ報告する。

各TiKVノードは、ノード全体に関する情報をPDへ定期的に報告する。

TiKV StoreとPDの間にはハートビートがある。PDはハートビートで各データストアがアクティブか、新しく追加されたデータストアがあるかを確認する。一方で、ハートビートには該当データストアの状態情報が含まれる。主に次のとおりである。

  • 総ディスク容量
  • 空きディスク容量
  • Region数
  • データ書き込み速度
  • 送受信したスナップショット数。レプリカはスナップショットでデータを同期する。
  • オーバーロードしていないか
  • ラベル情報。ラベルは上下関係を持つ一連のタグである。

各RaftグループのリーダーをPDへ定期的に報告

各RaftグループのリーダーとPDはハートビートで接続され、このRegionの状態を次のように報告する。

  • リーダーの状態
  • フォロワーの位置
  • オフラインレプリカ数
  • データ読み書き速度

この二種類のハートビートを通じて、PDはクラスタ全体の情報を収集し判断する。またPDは管理インターフェースを通じて追加情報を取得し、より正確な判断を行う。たとえば、データストアのハートビートが中断されると、PDはそれが一時的なものか永続的なものかを知ることができない。PDは一定時間、デフォルトでは30分だけ待つことができる。決められた時間を待ってもハートビートがなければ、PDは該当データストアがオフラインになったと判断し、そのデータストアのすべてのRegionを移動しなければならない。しかし、運用スタッフが手動でシステムをオフラインに設定した場合、そのスタッフは管理インターフェースを通じてPDにデータストアを使用できないことを知らせる必要がある。この場合、PDはデータストアのすべてのRegionを即座に移動する。

スケジューリングポリシー

情報を収集した後、具体的なスケジューリング計画を作るには、PDにいくつかのポリシーが必要である。

1. Regionのレプリカ数が正確である。

PDはRegionリーダーのハートビートを通じて、あるRegionのレプリカ数が要件を満たしていないことを発見すると、レプリカ追加/削除操作によりレプリカ数を修正する。これは次のような場合に発生する。

  • ノードが故障してすべてのデータを失い、一部Regionでレプリカが存在しない。
  • 失敗したノードが再び動作し、自動的にクラスタに参加する。この場合、重複レプリカが存在するため削除しなければならない。
  • 管理者がレプリケーションポリシーおよび最大レプリカ数設定を変更する。

2. 1つのRaftグループの複数レプリカが同じ位置に存在してはならない。

同じ位置であり、同じノードではない点に注意する必要がある。一般的にPDが保証できるのは、ノードが故障したとき多くのレプリカが失われる問題を避けるため、複数レプリカが同じノードに存在しないことである。実際の導入時には次のような要件が出る場合がある。

  • 同じ物理機器に複数ノードが配置される。
  • TiKVノードが複数サーバーに分散されている。サーバーが停止してもシステムを利用できると仮定する。
  • TiKVノードが複数IDCに分散されている。データセンターが停止してもシステムはそのまま利用できる。

基本的に共通位置属性を持ち、最小内蔵ユニットを構成するノードが必要である。このユニット内で複数Regionレプリカが共存しないことが望ましい。このとき、ノードにラベルを設定し、PDのロケーションラベルを設定して、どのラベルを位置識別子として指定するかを決める。レプリカをデプロイするとき、複数レプリカを保存するノードは同じ位置識別子を持たない。

3. レプリカが各データストアに均等に分散される。

各レプリカのデータ保存容量は固定されているため、各ノードのレプリカ数のバランスを保つと全体負荷がより均衡する。

4. リーダー数が各データストアに均等に分散される。

Raftプロトコルはリーダーを通じて読み書きを行うため、計算負荷は主にリーダーにかかる。したがってPDは、リーダーを別データストアへ分散して管理する。

5. ホットスポット数が各データストアに均等に分布する。

情報を送るとき、各データストアとRegionリーダーはキー読み書き速度のような、その時点のアクセス負荷に関する情報を渡す。PDはホットスポットを確認し、複数ノードへ分散する。

6. 各データストアのストレージ空間占有率がほぼ同じである。

各データストアは開始時に容量パラメータを指定するが、これはデータストアのストレージ空間上限を表す。PDはスケジューリング時にノードの残り空間を考慮する。

7. オンラインサービスに影響しないようスケジューリング速度を制御する。

スケジューリング動作はCPU、メモリ、ディスクI/O、ネットワーク帯域を消費するため、オンラインサービスに影響しないようにしなければならない。PDは進行中の作業数を制御し、デフォルト速度は保守的である。スケジューリングを加速したい場合、たとえばサービスアップグレード停止中、新ノード追加後、できるだけ早くスケジューリングしたい場合は、pd-ctlを使って手動で加速できる。

8. オフラインノードを手動でサポートする。

pd-ctlで手動にノードをオフラインに設定すると、PDは特定の速度制御範囲内でノードのデータを退避する。その後ノードをオフラインにする。

スケジューリング実装

では、スケジューリングの流れを見てみよう。

PDはデータストアとリーダーのハートビートを通じて常に情報を収集し、クラスタの詳細データを取得する。PDはこの情報とスケジューリングポリシーに基づいて操作順序を作成し、Regionリーダーから送られたハートビートを受信すると、このRegionに実行する作業があるか確認する。PDはハートビート応答メッセージを通じて、今後実行する作業をRegionリーダーへ返し、次のハートビートで実行結果を監視する。これらの作業はRegionリーダーへの提案にすぎず、実行が保証されるわけではない。実行するかどうか、いつ実行するかは、Regionリーダーが現在状態に基づいて決定する。

まとめ

ここでは、スケジューリングのための分散ストレージシステムを構築する際に何を考慮すべきか、そしてより柔軟にポリシーを拡張できるようにポリシーと実装を分離する方法について紹介した。

三つのブログ、ストレージ、コンピューティング、スケジューリングがTiDBの基本概念と実装原理を理解する助けになることを願う。