読み書き分離を有効化しない場合、アプリケーション内でプライマリノードおよび各読み取り専用ノードに対して個別の接続文字列を設定する必要があります。読み取り負荷のスケールアウトのために読み取り専用ノードを追加すると、アプリケーションコードが複雑化します。PolarDB for PostgreSQL (Compatible with Oracle) では、組み込みプロキシ機能により単一のクラスターエンドポイントを提供しており、書き込みリクエストはプライマリノードに送信され、読み取りリクエストは現在の負荷に基づいて利用可能なノード間で分散されます。この機能を利用するには、アプリケーションの変更は一切不要です。
仕組み
アプリケーションをクラスターエンドポイントに接続し、読み取りと書き込み(自動読み書き分離) モードを有効化します。
組み込みプロキシが受信した各リクエストを検査し、読み取りまたは書き込みのいずれかに分類します。
書き込みリクエストはプライマリノードに送信されます。読み取りリクエストは、各ノードのキューに積まれたリクエスト数で測定される負荷に基づき、利用可能なノード間で分散されます。
プロキシはノードの健全性を継続的に監視します。ノードが障害を起こした場合、またはレイテンシーが設定されたしきい値を超えた場合、プロキシは当該ノードへのリクエスト送信を停止し、トラフィックを健全なノードへ再配分します。ノードが復旧すると、プロキシは自動的にそのノードを再びトラフィック配分対象に追加します。
メリット
統一されたエンドポイント、アプリケーションの変更不要
単一の接続文字列ですべてのトラフィックを処理できます。アプリケーションの修正を伴わず、読み取り専用ノードを追加することで読み取り負荷のスケールアウトが可能です。
セッションレベルでの読み取り整合性
同一セッション内では、プロキシが各ノードのデータ同期進捗を追跡し、データが最新であるノードのみに読み取りリクエストをルーティングします。これにより、同一セッション内での古くなったデータ(stale read)の読み取りを防止します。
PREPARE ステートメントの均等な分散
書き込み操作を含む PREPARE ステートメントおよび対応する EXECUTE ステートメントは、プライマリノードにのみ送信されます。PREPARE ステートメントが読み取り専用操作を含む場合は、すべてのノードにブロードキャストされ、対応する EXECUTE ステートメントは負荷に基づいてルーティングされます。
外部プロキシよりも低いレイテンシー
組み込みプロキシはネイティブなクラスター構成要素として実行されるため、外部プロキシ構成で発生する余分な解析および転送ホップが不要になります。
ノードの健全性管理の自動化
すべてのノードに対して継続的なヘルスチェックが実行されます。ノードが障害を起こした場合、またはレイテンシーしきい値を超えた場合、トラフィックは即座に健全なノードへ再ルーティングされ、個々の読み取り専用ノードが障害を起こしてもアプリケーションの可用性が維持されます。
制限事項
サポートされていない接続方法およびステートメント:
レプリケーションモードによる接続。プライマリ/セカンダリ構成のデュアルノードクラスターをセットアップする場合は、プライマリノードのエンドポイントを直接使用してください。
一時テーブル名を用いた
%ROWTYPE属性の宣言:create temp table fullname (first text, last text); select '(Joe,von Blow)'::fullname, '(Joe,d''Blow)'::fullname;関数内での一時リソースの作成:
関数内で一時テーブルを作成し、その後の SQL ステートメントでそのテーブルをクエリした場合、「テーブルが存在しません」というエラーが発生することがあります。
関数内に
PREPAREステートメントが含まれる場合、対応するEXECUTEが「PREPARE ステートメント名が存在しません」というエラーで失敗することがあります。
ルーティング制限:
トランザクション内のすべてのリクエストはプライマリノードにルーティングされます。トランザクション終了後に負荷分散が再開されます。
COUNT()やSUM()などの集計関数を除く、関数を使用するすべてのステートメントはプライマリノードにルーティングされます。
読み書き分離のセットアップ
読み書き分離を利用するには、読み取りと書き込み(自動読み書き分離) モードでクラスターエンドポイントを設定します。
カスタムクラスターエンドポイントを作成するには、「カスタムクラスターエンドポイントの作成および変更」をご参照ください。
既存のクラスターエンドポイントの変更 (読み書きモードの変更、ノードの追加または削除など) については、「よくある質問」をご参照ください。
高度な設定
トランザクション分割 — トランザクション内の読み取り専用の文を、常にプライマリノードに送信するのではなく、読み取り専用ノードにルーティングします。この機能は、ワークロードのトランザクションが多く、読み取り専用ノードが十分に活用されていない場合に有効にします。詳細については、「トランザクション分割」をご参照ください。
整合性レベル — プロキシがノード間で読み取り整合性をどの程度厳密に適用するかを制御します。このレベルを緩和すると、読み取りスループットが向上します。一方、厳しく設定すると、陳腐化した読み取りのリスクが低減されます。詳しくは、「整合性レベル」をご参照ください。
よくある質問
レコードを挿入した直後に、なぜすぐに読み取れないのですか?
プライマリノードと読み取り専用ノード間のレプリケーションの遅延により、挿入された行がクエリを処理した読み取り専用ノード上にまだ反映されていない可能性があります。PolarDB はセッションの一貫性をサポートしており、同一セッション内で自身の書き込みをクエリできます。
読み取り専用ノードの負荷がゼロのままですが、なぜですか?
デフォルトでは、トランザクション内のすべてのリクエストがプライマリノードに送信されます。ベンチマークツール(例:sysbench)がすべてのクエリをトランザクションでラップしている場合、すべてのトラフィックがプライマリノードに集中し、読み取り専用ノードはアイドル状態となります。
sysbench でこの問題を回避するには、トランザクションをスキップします。sysbench 0.5 の場合は --oltp-skip-trx=on を、sysbench 1.0 の場合は --skip-trx=on を使用してください。
多数のトランザクションを処理する本番ワークロードでは、トランザクション内の読み取り専用文を読み取り専用ノードにルーティングするために、コンソールでトランザクション分割を有効にします。詳細については、「トランザクション分割」をご参照ください。
なぜ特定のノードに他のノードよりも多くのリクエストが集中するのですか?
リクエストは負荷(キューに積まれたリクエスト数)に基づいて分散されます。キューの深さが浅いノードほど、より多くのリクエストを受信します。これは想定通りの動作であり、プロキシは負荷の変動に応じてトラフィックを継続的に再バランス化します。
PolarDB はゼロ遅延の読み取りをサポートしていますか?
いいえ。通常の負荷下では、プライマリノードと読み取り専用ノード間に数ミリ秒のレプリケーションの遅延が発生します。クラスターエンドポイントの読み書きモードが「読み取りと書き込み(自動読み書き分離)」の場合、データが書き込まれた直後にゼロ遅延で読み取ることはできません。アプリケーションが書き込み後のゼロ遅延読み取りを要求する場合は、プライマリエンドポイントに接続してください。プライマリエンドポイントは常にプライマリノードを指し、最新のデータを即時に返します。
新しく追加した読み取り専用ノードは、読み書き分離に自動的に含まれますか?
読み取り専用ノードを追加した後に作成されたセッションでは、読み取りリクエストが新しいノードにルーティングされます。一方、ノード追加前に作成されたセッションでは、新しいノードは対象外となります。既存のセッションで新しいノードを含めるには、セッションを閉じて再開してください(例:アプリケーションを再起動)。