First Principles: クラウドのPostgreSQLの最適化 (2023/11/14)
First Principles: クラウドのPostgreSQLの最適化 (2023/11/14)
投稿者: Deepak Agarwal | Vice President & Distinguished Engineer, OCI Architecture
Pradeep Vincent | Senior Vice President and Chief Technical Architect, OCI
Oracleでは、OCI Exadata Database Service、OCI MySQL Database Service、MySQL Heatwave Databaseサービスなど、世界クラスのデータベース・システムとサービスを構築しています。これは、最近「First Principles」シリーズで取り上げたクラウド・スケール・サービスです。PostgreSQLは一般的なオープンソース・データベースであり、オラクルの目標は、OCIのお客様がクラウド・ワークロードを実行するためのデータベースを選択できるようにすることです。最近発表されたOCI Database for PostgreSQLでは、データベース・テクノロジに関するOracleの専門知識を身につけ、PostgreSQLサービスでクラウドスケールの世界クラスのOCI Databaseを構築しました。このブログ投稿では、OracleがPostgreSQLをクラウド・データベース・サービスにどのように適応させたかについて説明します。
PostgreSQLは、原子性、一貫性、分離および耐久性(ACIDプロパティとも呼ばれる)を提供する一般的なデータベース・ソフトウェアです。Oracle Cloud Infrastructure(OCI)では、何が優れたクラウド・データベース・エクスペリエンスをもたらすかについて意見を述べています。お客様は、データベース・インスタンスをオンデマンドで追加または削除し、コンピュートとストレージを互いに独立してスケーリングでき、データを失うリスクなくクラス最高の価格パフォーマンスを実現するようサービスを設計する必要があります。これらは、クラウド・ベースのデータベース・サービスの基本的なオンプレミスです。OCI PostgreSQLサービスから必要なキー・プロパティを次に示します:
- ゼロ・リカバリ・ポイント目標(RPO)とゼロ・データ損失: インフラストラクチャやシステム障害が発生した場合でも、データベースに保存されたデータは失われません。
- 一貫した高パフォーマンス: データベース・パフォーマンスは、レプリケーションなどのバックエンド・システム・タスクの影響を受けません。
- 水平読取りスケーリング: お客様は、書込みパフォーマンスに影響を与えることなく、読取り容量を簡単にスケール・アップおよびスケール・ダウンできます。
- 価格パフォーマンス: お客様のワークロードは低コストでより高速に実行されます。
- フル・オートメーション: お客様は、ソフトウェアのパッチ適用、ヘルス・モニタリング、データベースのフェイルオーバー、バックアップ、ストレージ・スケーリングなどのタスクにより、運用上のオーバーヘッドを削減できます。
vanilla PostgreSQLデータベース
図1: 顧客管理のPostgreSQLアーキテクチャ
ここからは、オープンソースのPostgreSQLを"vanilla PostgreSQL"と呼びます。vanilla PostgreSQLは強力で一般的なデータベースですが、お客様が直面する課題の一部を次に示します。
- プライマリフェイルオーバー中のデータ損失- ゼロ以外のRPO: 図1に示すように、お客様は、通常、可用性ドメインをまたいでデータベース・インスタンスをレプリケートします。このレプリケーションは、AD1のプライマリ・データベースからAD2の「レプリカ」に非同期的に実行されます。レプリカ・データベースがプライマリより遅れている可能性があります。プライマリに障害が発生し、レプリカが新しいプライマリに昇格された場合は、ラグが原因でデータが失われる可能性があります。データ損失の量は、プロモートされたレプリカが古いプライマリからどれくらい遅れていたかによって決まります。vanilla PostgreSQLのこの問題の解決策は、同期レプリケーション機能であり、パフォーマンスの大幅なオーバーヘッドのため、あまり一般的ではありません。
- 手動昇格と管理性の複雑さ: 高可用性を実現するために異なる可用性ドメインにレプリカを設定できますが、スタンバイ・レプリカをプライマリに昇格させることは手動の複雑なプロセスです。プロモートする新しい候補を選択するには、データの損失を最小限に抑えるために慎重に実行する必要があります。同様に、古いプライマリをクラスタに再追加するには、より多くの手動ステップが必要です。たとえば、古いプライマリでは、クラスタに再度参加する前に、pg_rewindなどのツールを使用してパージする必要がある、ローカルでコミットされた過剰なトランザクションがある場合があります。
- 読取りレプリカの作成にはコストがかかり、時間がかかります。vanilla PostgreSQLでは、新しいレプリカを作成するには、プライマリ上のデータのスナップショットを取得し、プライマリに追いつく必要があります。テラバイト単位の大きいデータベースの場合、これは高価で低速な操作です。アプリケーションからの読取り需要のバーストの処理を可能にするには、顧客はデータベース・リソースを過剰にプロビジョニングする必要があります。各レプリカにはデータベースのフル・コピーが必要であるため、複数のレプリカを実行するためのストレージ・コストは高額になる可能性があります。
OCI Database with PostgreSQLは、これらの課題を解決しました。
OCI Database with PostgreSQL- ハイレベル・アーキテクチャ
ここでは、OCI Database with PostgreSQLのアップレベル・アーキテクチャが、これらの課題をどのように解決し、OCIでのPostgreSQLの実行と管理を非常に簡単にする方法を説明します。OCI Database with PostgreSQLでは、レプリケーションと耐久性の問題を新しいDatabase Optimized Storage (DbOS)レイヤーにプッシュします。このレイヤーは、高スケールで高可用性とハイパフォーマンスのデータベース・サービスを実現することを目的として構築されています。DbOSは、データ・ブロックが3つの可用性ドメイン・リージョン内の複数の可用性ドメインにレプリケートされる、耐久性の高いネットワーク接続ストレージを提供します。単一ADリージョンでは、データは複数のフォルト・ドメインにレプリケートされます。クラスタ内のすべてのPostgreSQLノードは、同じネットワーク接続ストレージにアクセスします。各スタンバイ・レプリカは、データベースの独自のコピーを保持する必要がなくなりました。プライマリ・インスタンスは共有ストレージに書き込み、スタンバイ・レプリカ・インスタンスは同じ共有ストレージから読み取り、ユーザー問合せを実行します。
図2: OCI Database with PostgreSQL vs vanulla PostgreSQL
OCI Database with PostgreSQLで使用されるこの新しい共有ストレージ・アーキテクチャは、安全性、柔軟性、効率性、パフォーマンスという形で多くの利点を提供します。OCI Database with PostgreSQLは、その下にまったく異なるストレージ・アーキテクチャを持っていますが、vanilla PostgreSQLとの完全な互換性があります。そのため、既存のPostgreSQLワークロードをOCI Database with PostgreSQLにリフト・アンド・シフトしたり、簡単に元のワークロードに戻すことができます。DbOSは、組込みレプリケーションなどの高パフォーマンスのOCIブロック・ストレージ機能を活用する共有ファイル・システムです。
データベース最適化ストレージ(DbOS)の利点
新しく埋め込まれたDbOSレイヤーがPostgreSQLユーザーにとって非常に強力な利点である理由を詳しく説明します。
- 耐久性(ゼロ-RPO): DbOSは、マルチ可用性ドメイン・リージョン内の複数の可用性ドメインにまたがるデータをレプリケートし、可用性ドメイン全体の損失を回避できます。DbOSは、Quorumベースのレプリケーションを使用して、データ・ブロックをバックグラウンドでレプリケートします。プライマリ・ノードに障害が発生した場合、DBはレプリケートされたDbOSを使用する別のDBノードにフェイルオーバーでき、この新しく昇格されたプライマリPostgreSQLインスタンスはデータ損失なしで引き継ぐことができます。古いプライマリで以前にコミットされたすべてのトランザクションが、新しいプライマリに存在します。DbOSレイヤーはレプリケーションを実行するため、耐久性のために複数のレプリカ・インスタンスを実行する必要はありません。たとえば、レプリカなしでPostgreSQLインスタンスで単一ノードのOCIデータベースを実行できますが、耐久性は犠牲にしません。この単一ノード設定でも、RPOがゼロであることが保証されます。
- 高可用性(99.99%): OCI Database with PostgreSQLを使用すると、数分でプライマリをクラスタ内の別のレプリカに自動的にフェイルオーバーできます。プライマリ・フェイルオーバーは、リカバリ時間目標にわずか数分かかるため、アプリケーションに対して迅速かつ透過的です。フェイルオーバーを透過的に有効にするために、プライマリ・エンドポイントは、新しいプライマリに自動的に移動される浮動IPアドレスとして設定されます。アプリケーションへの構成変更を必要とせずに、フェイルオーバー後にアプリケーションによって新しいプライマリへのデータベース接続が自動的に再確立されます。vanill PostgreSQLとは異なり、プライマリ・フェイルオーバーの開始時にレプリケーション・ラグおよびデータ損失について心配する必要はありません。データ損失を最小限に抑えるために、特定のレプリカを手でピッキングする必要はありません。クラスタ内のすべてのインスタンスが同じストレージを共有するため、フェイルオーバー時に、新しいプライマリはデータ損失ゼロが保証されます。
- 弾力性: データベース・ストレージはすべてのノードで共有されるため、レプリカを迅速に作成または削除して、ユーザーの問合せワークロードの要求を満たすことができます。vanilla PostgreSQLとは異なり、プライマリ上のデータのスナップショットを取得し、レプリカ・ノードにコピーして新しいスタンバイPostgreSQLインスタンスを起動する必要はありません。そのため、OCI Database with PostgreSQLでコンピュート・インスタンスを起動するのと同じくらい迅速にスタンバイ・レプリカを作成できます。
- 読取りレプリカの水平スケーリング: レプリカ・ノードはデータベース・ストレージをOCI Database with PostgreSQLで共有するため、データベース・クラスタ内のレプリカの数に関係なく、データベースのコピーを1つのみ保持する必要があります。その結果、OCI Database with PostgreSQLは、クラウドでのvanilla PostgreSQLの実行と比較して、ストレージ・コストを大幅に削減します。また、OCIのPostgreSQLサービスは、コストを最小限に抑えるために、自動スケーリングによるデータに対する従量制の価格設定モデルを提供します。
- レプリカ・ラグが低い: レプリケーション・ラグは、vanilla PostgreSQLの読取りレプリカ設定において大きな課題です。レプリカはプライマリによって行われたすべての変更をリプレイして永続化する必要があるため、特にネットワーク・パーティションでは遅延が発生しやすくなります。共有ストレージでは、レプリカによる処理が大幅に少なくなります。キャッシュ内のページに変更を適用するだけで、これらの変更を保持する必要はありません。このアーキテクチャでは、通常、レプリケーション・ラグはミリ秒単位であり、読取り問合せをほぼリアルタイムで実行または完了できます。
- 効率的なレプリケーション: OCI Database with PostgreSQLは、ストレージ・レイヤーでレプリケーションを実行します。したがって、プライマリ・インスタンスは、先行書込みログ(WAL)レコードをすべてのレプリカに物理的に出荷する必要はありません。かわりに、レプリカに新しい変更を通知し、個々のレプリカが共有ストレージから直接最新のWALレコードを読み取ります。これにより、プライマリの負荷が最小限に抑えられ、より効率的に多数のレプリカにスケーリングできます。
この実験では、AD間のレプリケーションを使用するOCI Database with PostgreSQLは、vanilla PostgreSQLの同期レプリケーションの2倍以上高速でした。
DbFS: 単一ライター複数リーダーのファイルシステム
Linux上のvanilla PostgreSQLは、カーネルでサポートされている標準ファイルシステム(通常はext4またはxfs)を使用します。このようなファイルシステムは、クラスタ内のすべてのノードが同じ基礎となるストレージまたはファイルシステムを共有するOCI Database with PostgreSQLなどの分散設定用に設計されていません。図3に示すように、OCI DatabaseでPostgreSQLとともに使用されるファイルシステム実装の重要な側面をいくつか確認します。
図3: OCI Database with PostgreSQLアーキテクチャ
- シングルライター/マルチリーダー: DbOS共有ストレージ上でOCI Database with PostgreSQLを実行するには、複数のノードが同時にファイルシステムにアクセスできるようにするカスタム・ユーザー・モードのDbOSファイルシステム(DbFS)を構築しました。具体的には、プライマリ・データベースおよび複数の読取りレプリカ・データベースをサポートするために、単一ライター複数リーダーのアクセス・モデルをサポートしています。
- Metadata-based replication: プライマリ・ノードはDbFSを読取り/書込みモードでマウントし、読取り/レプリカ・ノードはDbFSを読取り専用モードでマウントします。プライマリノードによって行われたファイルシステムへの変更は、レプリカノードから認識されます。データはストレージ・レイヤーに格納され、ファイル・システムはメタデータを管理します。ストレージは共有されるため、プライマリからのデータレプリケーションはメタデータのみをレプリケートすることによって実現されます。ファイルの作成やファイルへのエクステントの割当てなど、ファイルシステム・メタデータに対するすべての変更は、DbFSにジャーナルされます。ジャーナル更新はレプリカノードに出荷されます。レプリカ・ノードは、ジャーナル・エントリをリプレイしてインメモリー・メタデータを保持し、最新の状態にします。これは、vanilla PostgreSQLがWAL更新を介して物理レプリケーションを実行する方法と同様です。
- LSN対応ストレージ: DbFSのデータおよびメタデータの変更は、データベース・ログ順序番号(LSN)でタグ付けされます。不透明なデータなどのブロックを処理する従来のファイル・システムとは異なり、DbFSは個々のデータベース・ブロックのLSNを追跡します。同じブロックの複数のバージョンを異なるLSNに格納します。これにより、PostgreSQLは、所定のLSNでDbFSからブロックを読み取り、「一貫性読取り」を実現できます。ファイルの作成や削除などのMetadata操作も、対応するデータベースLSNでタグ付けされます。ストレージはすべてのデータベース・インスタンス間で共有されるため、DbFSはプライマリによるファイル削除を処理する必要があります。プライマリでファイルを削除すると、遅延している一部のレプリカ・ノードがそれらのファイルにアクセスする可能性があるため、ストレージからすぐに削除することはできません。DbFSは、このようなファイルをオンブストーンしますが、レプリカからは引き続きアクセスできます。レプリカのLSNが先に進むと、これらの墓石ファイルはバックグラウンドで完全に削除されます。
- 従量制ストレージ・モデル: DbFSはオンライン・サイズ変更をサポートしているため、PostgreSQLが使用するストレージ領域に応じて、基礎となるストレージをスケール・アップおよびスケール・ダウンできます。オンライン・サイズ変更は、純粋なメタデータのみの操作であるため、迅速です。データ・ストレージ領域が減少すると、ストレージも自動的にスケール・ダウンされます。
共有記憶域からの一貫性読取りの実現
図4: vanilla PostgreSQLレプリケーション
レプリカが読取り問合せを満たすには、そのレプリカの現在のReplayLSN時点で一貫性のあるページのバージョンを読み取れる必要があります。これは、分割やマージなどの構造的変更が並行している可能性があるBツリーなどの索引構造を横断する場合に重要です。vanilla PostgreSQLでは、LSNはログ順序番号で、ReplayLSNはレプリケーション・プロセスの一部として実行されるWALリプレイ・アクティビティ中のLSNです。vanilla PostgreSQLでは、各レプリカに独自のストレージがあり、これは各レプリカが読取り一貫性を個別に維持できることを意味します。図4は、vanilla PostgreSQLにプライマリ・データベースとレプリケート・データベースの別々に格納されたデータがあることを示しています。プライマリのデータはLSN 100で保持され、LSN 110で最新のデータが書き込まれており、レプリカはLSN 90で後続しています。ブロック0の読取りリクエストがレプリカに送信されると、LSN 90の最新データが読み取られます。
次に、データベース対応の共有ストレージ・レイヤーを使用するDbFSによって、この同じ状況がどのように処理されるかを見てみましょう。
図5: OCI Database with PostgreSQL - レプリカのプライマリ・アヘッド・シナリオ
レプリカが特定のLSNでページを読み取ることを許可するために、DbFSは、ページの複数のバージョンを様々なLSNに保持します。プライマリがチェックポイントまたはキャッシュ削除の一部としてページをフラッシュする場合、レプリカがまだ必要な場合、DbFSは前のコピーを上書きしません。ログ構造化ファイル・システムに類似したスキームでは、ブロックは新しいオフセットに書き込まれ、ブロックの複数のバージョンが読取りレプリカで使用可能になります。DbFSがすべてのレプリカがLSNを過ぎて移動し、ブロックが不要になったと判断すると、古いバージョンが最終的に破棄されます。
図5では、プライマリは最初にLSN 90でブロック0を永続化し、その後LSN 100で永続化しました。レプリカはまだLSN 90にあるため、プライマリはLSN 100を別の場所に書き込み、LSN 90のブロック0を上書きしません。プライマリおよびレプリカ用にブロック0の別々のバージョンを格納すると、レプリカごとに読取り一貫性が保証されます。
図6: OCI Database with PostgreSQL - プライマリは永続化されていない最新のデータ・シナリオ
プライマリは、レプリカのReplayLSNの時点でページを永続化していない場合、共有ディスクから古いバージョンのページを読み取り、このページに適用可能なWALレコードをオンザフライでリプレイして、レプリカの現在のReplayLSNの時点でこのページを最新にします。このWALリプレイを効率的にするために、各レプリカは、ページ番号で索引付けされ、最後のチェックポイント以降に保持されるすべてのWALレコードのインメモリー索引を保持します。
図6では、プライマリはLSN 50でブロック0を永続化し、ブロック0のインメモリーデータはLSN 110になります。レプリカは、LSN 90がインメモリー状態になるまで正常にリプレイされました。その結果、レプリカはブロック0の読取りリクエストをLSN 90のDbOSにリクエストします。DbOSには現在、LSN 50でデータが永続化されています。DbOSは、LSN 90までWALレコードを適用し、LSN 90のデータをレプリカに返します。WAL索引を使用すると、DbOSにより、プライマリがデータをストレージに永続化していない場合でも、一貫性のあるデータをレプリカに正常に返すことができます。
プライマリ・フェイルオーバー
高可用性を実現するために、OCI Database with PostgreSQLは、異常なプライマリ・インスタンスを自動的に検出し、既存の読取りレプリカにフェイルオーバーするか、読取りレプリカがない場合は新しいデータベース・インスタンスを起動します。フェイルオーバーのプロセスは、異常なプライマリが共有ストレージへの新しい変更を停止するだけでなく、共有ストレージへの処理中の I/Oリクエストが取り消されることを保証する必要があります。ネットワーク・パーティション化を伴う一部の障害シナリオでは、異常なプライマリに外部からアクセスできない可能性がありますが、共有ストレージを変更することは可能です。このような複雑なケースを処理するために、DbFSはブロック・レイヤー予約(NVMe予約またはSCSI-3永続予約)を使用して、古いプライマリをフェンスアウトし、常に単一のプライマリが存在することを確認します。永続予約またはNVMe予約では、予約を保持するユーザーは共有ストレージへの書込みが許可されます。フェイルオーバー中、新しいプライマリは予約を取得し、古いプライマリをプリエンプトします。これを行うと、ストレージ・サブシステムは、古いプライマリからのすべてのリクエストを拒否します。
ストレージのさらなる最適化
共有ストレージの最適化に加えて、OCI Database with PostgreSQLでは、パフォーマンスをさらに向上させるために次の最適化を実装しました。
原子書込み: DbOSは、破損書込みの排除など、既知のデータベース・パフォーマンス・リスクに対する最適化を実装します。通常、ほとんどのデータベースでは、基礎となるストレージの「アトミック書込みユニット」サイズ(通常は512Bまたは4KB)と一致しないページ・サイズ(PostgreSQLは8KB)をデータベースで使用する場合に発生する「破損書込み」に対するある種の保護が必要です。たとえば、PostgreSQLは、最初に8KBページ全体をWALに書き込みます(最後のチェックポイント以降にページに対する最初の変更である場合)。その後、ページをディスクにフラッシュします。ページ書込みが破損している場合、PostgreSQLは、以前にWALで記述したフル・ページの使用に戻り、害は発生しません。 しかし、この保護は価格で提供されます- それはWALのバローニングを引き起こし、計画は計画外のフェイルオーバー中の回復時間を最小限に抑えるために必要な頻繁なチェックポイントによって問題が悪化しています。PostgreSQLページのアトミック書込みサポートをDbOSに実装しました。ストレージ・レイヤーは、既存のページを上書きしません。かわりに、ログ構造化手法を使用して、常にページをディスク上の新しい場所に書き込み、論理ファイル・オフセットからディスクの場所へのマッピング・レイヤーを維持します。古いバージョンのページは定期的にガベージ・コレクションされます。これにより、二重書込みが回避されます。
最適化されたページ・キャッシュ: OCI Database with PostgreSQLでは、汎用Linuxカーネルのページ・キャッシュに依存するvanilla PostgreSQLとは異なり、専用のキャッシュ・レイヤーが使用されます。OCIのページ・キャッシュ実装には、次のような多くの最適化があります。
- PostgreSQLワークロードに合せて調整されたカスタム・プリフェッチ・ロジック
- PostgreSQL共有バッファおよびページ・キャッシュ内の二重キャッシュ・ページを回避
- データ・ページのプリフェッチによるPostgreSQLリカバリの高速化
ストレージ・レベルのバックアップ: vanilla Postgresでは、データベース・バックアップを維持するために、WALがオブジェクト・ストレージにコピーされ、ファイル・システムの定期的なスナップショットが取得されます。このプロセスでは、プライマリ・ノード上のネットワークとCPUの両方が使用されます。OCI Database with PostgreSQLは、バックアップをストレージ・レイヤーに委任し、バックアップのネットワークおよびCPUオーバーヘッドを排除します。
まとめ
OCI Database with PostgreSQLサービスは、前述したコスト、パフォーマンス、スケール、可用性および耐久性の形式で大きなメリットを提供します。これらの利点の大部分を達成するための鍵は、PostgreSQLを最適化してクラウド・スケールでより効果的に機能するように専用のDbOSおよびDbFSに基づいています。
主なポイント
- OCI Database with PostgreSQLでは、すべてのデータベース・ノードが同じ基礎となるDbOSを共有する専用の共有ストレージ・アーキテクチャを使用します。
- DbOSは、プライマリ・フェイルオーバー中(ゼロ-RPOフェイルオーバー)のデータ損失なしで、リージョン間でデータを自動的にレプリケートします。
- OCI Database with PostgreSQLでは、読取りワークロードが水平方向にスケーリングされ、クラスタ内のすべてのレプリカで高パフォーマンスの一貫性読取りが可能になります。
- 共有ストレージは、データの複数のコピーを格納する必要がなく、データ・サイズが縮小するとストレージが自動的にスケール・ダウンされるため、お客様にとって大幅なコスト削減を実現します。
Oracle Cloud Infrastructure(OCI)Engineeringは、エンタープライズ顧客にとって最も要求の厳しいワークロードを処理するため、クラウド・プラットフォームの設計について異なる考え方をするようになりました。Pradeep VincentとOracleのその他の経験豊富なエンジニアが主催する、このFirst Principlesシリーズの一部として、これらのエンジニアリングについて詳しく説明します。
詳細は、次のリソースを参照してください。
コメント
コメントを投稿