このトピックでは、PolarDB for PostgreSQL の先行ロギング (WAL) 並列再生機能について説明します。
前提条件
PolarDB for PostgreSQLクラスターは、次のいずれかのエンジンを実行します。
PostgreSQL 14 (バージョン14.5.1.0以降)
PostgreSQL 11 (バージョン1.1.17以降)
次のいずれかのステートメントを実行して、PolarDB for PostgreSQLクラスターのリビジョンバージョンを表示できます。
PostgreSQL 14
select version();
PostgreSQL 11
show polar_version;
背景情報
PolarDB for PostgreSQL クラスターは、プライマリノードと1つ以上の読み取り専用ノードで構成されます。 読み取り専用ノードが読み取り要求を処理している間、バックグラウンドワーカープロセスとバックエンドプロセスはLogIndexを使用して異なるバッファでWALを再生し、WALレコードの並列再生を実現します。
WALレコードの再生は、PolarDBクラスタの高可用性に不可欠であるため、標準のWAL再生プロセスでこの並列ログ再生方法を使用することは、システムの最適化に貢献します。
WALレコードの並列再生は、次のシナリオで役立ちます。
プライマリノード、読み取り専用ノード、およびセカンダリノードのクラッシュリカバリ。
読み取り専用ノードでLogIndexデータを使用したバックグラウンドワーカープロセスによるWALレコードの継続的な再生。
セカンダリノードで実行されているスタートアッププロセスによるWALレコードの継続的な再生。
用語
ブロック: データブロック。
WAL: 先行書き込みロギング。
タスクノード: サブタスクが実行されるノード。 タスクノードは、一度にサブタスクを受信して実行する。
Taskタグ: サブタスクタイプの識別子。 同じタイプのサブタスクは特定の順序で実行されます。
タスク保持リスト: 並列実行フレームワークでリプレイサブタスクをスケジュールするために各子プロセスによって使用されるリスト。
WALレコードの並列再生の仕組み
概要
WALレコードは、複数のデータブロックに対する変更を含むことができる。
ith
のWALエントリ (そのLSNがLSN i
である) がm
データブロックへの変更を記録すると仮定する。 いくつかの概念は次のように定義されます。ith
WALエントリに記録された変更されたデータブロックのリストは、Blocki=[Blocki,0, Blocki,1,..., Blocki,m]
として定義される。最小再生サブタスクは、
Taski,j=LSNi->Blocki,j
として定義され、Blocki,j
上のith
WALレコードの再生を示す。m
個のデータブロックへの変更を記録するi番目のWALエントリは、m
個のリプレイサブタスクのグループとして表すことができる:TASKi,∗=[Taski,0, Taski,1, ..., Taski,m]
。複数のWALエントリは、リプレイサブタスクグループのコレクションとして表されます。
TASK∗,∗=[Task0,∗, Task1,∗, ..., TaskN,∗]
。
Task,
では、サブタスクは常に前のサブタスクに依存するとは限りません。サブタスクグループのコレクションが
TASK∗,∗=[Task0,∗,Task1,∗,Task2,∗]
であると仮定します。Task0,∗=[Task0,0,Task0,1,Task0,2]
Task1,∗=[Task1,0,Task1,1]
Task2,∗=[Task2,0]
および Block0,0=Block1,0, Block0,1=Block1,1, and Block0,2=Block2,0.
[Task0,0,Task1,0], [Task0,1,Task1,1], and [Task0,2,Task2,0]の3組の再生サブタスクを並行して実行できます。
要約すると、WALレコードを表す多くのサブタスクグループは、最終結果の一貫性に影響を与えることなく並列に実行できます。 この考えに基づいて、PolarDBは並列タスク実行のためのフレームワークを導入し、このフレームワークをWALレコード再生プロセスに組み込んだ。
並列タスク実行のフレームワーク
共有メモリは, 同时プロセス数に応じて均等に分割されます。 各セグメントは、プロセスに割り当てられた循環キューである。 循環キューの深さは、パラメータで構成される。
Dispatcherプロセス
指定したプロセスにタスクを分散して同時スケジューリングを制御します。
循環キューから実行されたタスクを削除します。
プロセスプール
プール内の各プロセスは、対応する循環キューからタスクを取り出し、その状態に基づいてタスクを実行するかどうかを決定する。
タスク
循環キューはタスクノードで構成されます。 タスクノードには、
idle
、running
、hold
、finished
、removed
の5つの状態があります。idle
: タスクノードにタスクが割り当てられていません。running
: タスクノードにタスクが割り当てられており、タスクは実行中または実行されます。hold
: タスクノードには、別のタスクに依存するタスクが割り当てられており、その実行を待つ必要があります。finished
: タスクノードのタスクが実行されました。removed
: ディスパッチャプロセスは、タスクとその前提条件タスクを循環キューから削除しました。 タスクノード上のタスクが終了すると、その前提タスクも終了する必要があります。したがって、すべてを安全に削除できます。 このメカニズムは、ディスパッチャプロセスが依存関係の順にタスクの実行結果を処理することを保証することができる。
前の図では、黒でマークされた状態遷移はディスパッチャプロセスによって実行され、オレンジでマークされた状態遷移はプロセスプールによって実装されます。
Dispatcherプロセス
ディスパッチャプロセスは、タスクHashMap、タスクランニングキュー、およびタスクアイドルノードの3つの重要なデータ構造を有する。
Task HashMap: タスクタグとタスク間のハッシュマッピングを記録します。
各タスクには、タスクタグが割り当てられる。 依存タスクは同じタスクタグを持ちます。
タスクノード上のタスクに前提条件がある場合、タスクノードは、前提条件タスクが完了するまで保留状態になります。
タスク実行キュー: 実行中のタスクを記録します。
Task Idelノード: プロセスプール内のさまざまなプロセスの
idle
のタスクノードを記録します。
ディスパッチャーのスケジューリング戦略:
好ましくは、同じタスクタグを有するタスクを実行するプロセスにタスクを割り当て、即時の前提条件である。 この目的は、依存タスクを同じプロセスに割り当てることにより、プロセス間の同期のオーバーヘッドを減らすことです。
優先プロセスにフルキューがある場合、または同じタスクタグでタスクを実行しているプロセスがない場合、ディスパッチャはプロセスプールから順番にプロセスを選択し、そのタスクを
idle
状態のタスクノードに割り当てます。 このように、タスクは異なるプロセスに均等に分散されます。
プロセスプール
並列実行メカニズムは、同じタスクノードデータ構造を持つタスクに使用されます。
SchedContext
は、プロセスプールの初期化中に、タスク実行の関数ポインターを指定するように設定されます。TaskStartup: プロセスが割り当てられたタスクを実行する前の初期化アクション。
TaskHandler: 割り当てられたタスクを実行します。
TaskCleanup: プロセスが終了する前のクリーンアップアクション。
プロセスプール内のプロセスは、循環キューからタスクノードを取り出す。 タスクノードが
hold
状態にある場合、プロセスは、タスクノードをhold list
の末尾に挿入する。 タスクノードがrunning
の状態の場合、プロセスはTaskHandler
を呼び出します。 これが失敗した場合、プロセスはデフォルトで3のホールドカウントを割り当て、このタスクノードをhold list
の先頭に追加します。このプロセスは、
hold list
を先頭から末尾までスキャンする。running
のタスクノードを見つけると、プロセスはタスクノードの保留カウントをチェックします。 ホールドカウントが0の場合、タスクが実行されます。 保持カウントが0より大きい場合、保持カウントは1だけ減少される。 保留リストのスキャンプロセスを次の図に示します。
WALレコードの並列再生
LogIndexは、WALエントリと変更されたデータブロックとの間のマッピングを記録する。 LSNで取得できます。 PolarDBでは、読み取り専用ノードでWALレコードが継続的に再生される場合の並列タスク実行のフレームワークが導入されます。 LogIndexデータとともに使用されるWALの並列再生は、プライマリノードと読み取り専用ノード間のデータ同期を高速化します。
ワークフロー
起動プロセスはWALを解析し、WALを再生せずにLogIndexデータを構築します。
LogIndex BG Writerプロセスは、並列リプレイフレームワークのディスパッチャプロセスとして、LSNを使用してLogIndexデータを取得し、リプレイサブタスクを構築し、それらをプロセスプールに割り当てます。
並列ワーカープロセスは、リプレイサブタスクを実行し、データブロック上の単一のWALレコードをリプレイする。
バックエンドプロセスは、データブロックの読み取り中に、ページタグを使用してLogIndexデータを取得し、LogIndexデータに従ってこのデータブロックにマッピングされたすべてのWALレコードを再生します。 次の図は、並列再生のワークフローを示しています。
ディスパッチャープロセス: LSNを使用してLogIndexデータを取得し、ページタグとそれぞれのLSNを順次リストし、タスクノードとして
{LSN -> Page tag}
のマッピングを作成します。ページタグは、同一のタスクタグを共有するタスクを割り当てられたタスクノードを識別する。
各タスクノードは、リプレイのためにプロセスプール内の並列ワーカープロセスに分散されます。
設定
読み取り専用ノードのpostgresql.confファイルで次のパラメーターを設定して、WALレコードの並列再生を有効にします。
polar_enable_parallel_replay_standby_mode = ON