このトピックは、PolarDB for PostgreSQL Distributed Edition の利用を準備しているワークロードを対象としています。クラスター計画、アカウントとデータベースの準備、テーブル分散、検証とスケーリングの手順を説明し、本番稼働前の準備を完了します。この手順により、通常のデータベースを手動で作成したり、不適切なエンドポイントに接続したりすることによる、分散機能が有効にならない、パフォーマンスが低下する、またはアプリケーションが利用できなくなるといった障害を防ぎます。
ビジネスシナリオ
PolarDB for PostgreSQL 分散版クラスターを購入した後、接続、データシャーディング、テーブルスキーマ、およびトランザクションの設計をまとめる必要があります。このチュートリアルでは、ドキュメントワークロードを例として使用します。documents テーブルはドキュメント情報を格納し、document_revisions テーブルはドキュメントの改訂履歴を格納します。両方のテーブルは doc_id でシャーディングされ、同じコロケーショングループに配置されます。document_status テーブルは、読み取りが多く書き込みが少ないステータス辞書であるため、レプリケートテーブルとして作成されます。
ソリューションアーキテクチャ
コンピューティングノード (CN) は、アプリケーションリクエストを受信し、SQL 文の実行方法を決定し、データノード (DN) にタスクをディスパッチして、結果を集約します。CN はまた、テーブルスキーマやシャードの場所などの情報を維持し、レプリケーションテーブルへの読み取りと書き込みにも関与します。DN はシャードデータを格納し、クエリを実行します。クラスターを計画する際は、2 種類のノードのリソース要件を個別に評価します。
CN の負荷は主に、接続の処理、実行計画の生成、結果の集約から生じ、接続数、同時リクエスト数、SQL の複雑さの影響を受けます。
DN の負荷は主に、クエリの実行、データの格納、ディスクの読み書きから生じ、データ量やリクエストが少量のデータに集中するかどうかの影響を受けます。複数の DN にデータを分散させても、すべての SQL 文が等しく恩恵を受けるわけではありません。分散列、結合関係、トランザクションスコープは、実際のルーティングと実行オーバーヘッドに引き続き影響します。
操作手順
クラスターとエンドポイントの計画
CN と DN の数の選択
初期評価として、1 つの CN と 4 つの DN で負荷テストを開始し、実際の負荷に基づいて調整します。ワークロードの特性に応じて、1:2 から 1:8 の間の CN 対 DN 比を評価します。この比率は計画の参考用であり、固定された比率や製品の制限ではありません。
エンドポイントの選択
購入が完了すると、クラスター詳細ページにプライマリエンドポイントとデフォルトの分散プロキシエンドポイントが表示されます。業務上の用途と次の表の前提条件に基づいて、接続エントリポイントを選択してください。
エンドポイント | 接続範囲 | 推奨用途 |
[プライマリエンドポイント] | プライマリ CN | テーブルの作成、テーブルの分散、DML 操作の実行。 |
デフォルトの分散プロキシエンドポイント | クラスター内の CN。CN の追加または削除に応じて調整されます。 | テーブルの作成と分散。分散テーブルの読み取りおよび書き込みリクエストを複数の CN に分散します。 |
カスタムエンドポイント | 選択した CN のセット | ワークロードごとに接続エントリポイントを分離し、負荷分散されたリクエストを受信する CN を制御します。 |
カスタムエンドポイントが新規追加された CN を自動的に含めるかどうかは、エンドポイントの作成または設定時に選択したオプションによって異なります。カスタムエンドポイントは CN レイヤーでリクエスト負荷を分離できますが、アプリケーションテーブルは引き続き DN リソースを共有できます。データ分散と合わせて、全体的な分離効果を評価してください。
レプリカ CN がアタッチ済み、または自動的にアタッチされるエンドポイントでは、通常のローカルテーブルの読み書きはできません。アプリケーションが、まだ分散されていない通常のローカルテーブルの読み書きを必要とする場合は、レプリカ CN がアタッチ済み、またはスケールアウト中に新規追加されたレプリカ CN を自動的にアタッチする分散プロキシエンドポイントやカスタムエンドポイントを使用しないでください。このようなエンドポイントを使用する前に、アプリケーションの読み書きで使用されるすべてのテーブルを分散させる必要があります。これには、次のタイプが含まれます。
これらのテーブルはすべての CN で表示可能です。分散されていない通常のローカルテーブルは、それが存在する CN でのみ参照できます。リクエストがレプリカ CN にルーティングされると、テーブルが見つからないため、読み取りと書き込みは失敗します。
重要現在プライマリ CN のみを指している場合でも、通常のローカルテーブルに依存するアプリケーションの読み書きには、デフォルトの分散プロキシエンドポイントを使用しないでください。スケールアウト前は、これらのテーブルへのアクセスは正常に動作するように見える場合があります。スケールアウト中に新しい CN が追加されると、デフォルトの分散プロキシエンドポイントは新しいレプリカ CN を自動的にアタッチし、これらのテーブルが見つからないため、レプリカ CN にルーティングされたリクエストは失敗します。
アプリケーションが引き続き通常のローカルテーブルの読み書きを行う必要がある場合は、テーブルが存在する CN に接続します。 テーブルがプライマリ CN 上にある場合は、プライマリエンドポイント を使用します。 関連テーブルを分散した後、分散プロキシエンドポイントまたはカスタムエンドポイントに切り替えます。
アカウント、データベース、ネットワークの準備
PolarDB コンソールでアカウント、データベース、ネットワークの準備を完了してください。
アカウントの作成:オブジェクト管理用に 特権アカウント を作成してから、ワークロードの要件に応じて 標準アカウント を作成します。アプリケーションは 標準アカウント を使用し、
GRANTステートメントを介して必要な権限を受け取ります。データベースの作成: 分散クラスターに含めることができる分散データベースは 1 つだけです。その所有者を、以前に作成した特権アカウントに設定します。その後のすべてのテーブル作成、権限付与、分散操作は、このデータベースに接続して行います。
ホワイトリストの設定:アプリケーションマシンの実際のアクセスアドレスをクラスターのホワイトリストに追加し、アプリケーションマシンがデータベースエンドポイントに到達できることを確認します。
データベースに完全な分散機能を持たせるには、PolarDB コンソールのデータベース機能でデータベースを作成してください。 コンソールの SQL ウィンドウで直接CREATE DATABASEを実行しないでください。 この方法では、通常の集中型 PolarDB for PostgreSQL データベースしか作成されません。
以下の例では、データベース appdb と標準アプリケーションアカウント app_user を使用し、両方ともコンソールで作成済みであることを前提としています。初期化の例は、プライマリエンドポイント または分散プロキシエンドポイントを介して分散データベース appdb に接続された 特権アカウント によって実行されます。どちらのエンドポイントも、テーブルの作成、権限の付与、およびテーブルの分散に使用できます。アプリケーションの読み取りと書き込みに関係するすべてのテーブルが分散された後、アプリケーションは分散プロキシエンドポイントを介して本番トラフィックを処理できます。
アプリケーションのアクセスパターンに合わせたシャーディングの設計
テーブルを分散する前に、頻繁なクエリ、変更、トランザクションの対象となるデータを確認します。次に、データをシャードに分散させるための分散列として使用する列を選択し、テーブルタイプを決定します。
アプリケーションテーブルを分散テーブルとして作成します。
辞書テーブル、設定テーブル、およびその他の読み取りが多く書き込みが少なく、複数のアプリケーションテーブルと結合する必要がある小規模な共有テーブルは、レプリケーションテーブルとして作成できます。すべての CN と DN は完全なコピーを格納するため、DN は自身のノード上のデータを使用して結合クエリを完了できます。レプリケーションテーブルへの書き込みは、複数のノード間で同期する必要があります。テーブルが小さいという理由だけで負荷テストを省略し、頻繁な書き込みにレプリケーションテーブルを使用しないでください。
分散列と頻繁なクエリおよび更新のマッチング
頻繁に実行される SQL ステートメントでは、データベースが doc_id に基づいてリクエストを単一のシャードにルーティングできるよう、可能な限り分散キー列に WHERE doc_id = $1 のような等価条件を含める必要があります。
UPDATEおよびDELETEでは、最も外側のWHERE句で対象テーブルの分散キー列の条件を指定する必要があります。サブクエリ内にのみ分散キー列の条件を追加しないでください。アプリケーションのパラメーターの型は、分散列の型と一致させる必要があります。可能な限り、分散列に関数を適用したり、型をキャストしたりすることは避けてください。そうしないと、データベースがリクエストを単一のシャードにルーティングできず、より多くのシャードにアクセスする必要が生じる可能性があります。
分散列は、多数の個別値を持ち、null を含まず、比較的均等な負荷を分散し、アプリケーションによって変更されないようにする必要があります。ステータスやカテゴリなど、値が少ないフィールドは使用しないでください。
また、データ量とリクエストのホットスポットを確認してください。
doc_idに多くの値がある場合でも、リクエストの大半が同じドキュメントに集中していると、DN を追加してもその分散列の値に対する負荷を複数の DN に分散させることはできません。
頻繁に結合される大規模テーブルのコロケーションの維持
頻繁に結合される大規模なテーブルの場合は、ビジネス上の意味とデータ型が同じ分散キー列を選択し、同じ数のシャードを使用し、テーブルを同じ co-location グループに配置することを推奨します。 co-location グループを使用すると、異なるテーブルにある、分散キー列の値が同じ行が同じ DN に格納されるようになります。 たとえば、ドキュメントとリビジョンレコードを doc_id でシャーディングし、co-locate した場合、1 つのドキュメントのデータを同じ DN 上で結合できるため、ノード間のデータ転送が削減されます。 JOIN 条件では、a.doc_id = b.doc_id のように、分散キー列間の等価関係を指定する必要があります。
主キー、一意性制約、外部キーの確認
分散テーブルのプライマリキーと一意制約には、分散カラムを含める必要があります。たとえば、doc_id でシャーディングされた詳細テーブルは、PRIMARY KEY (doc_id, id) を使用できます。
UNIQUE (id) を UNIQUE (doc_id, id) に変更すると、データベースは、id が同じ doc_id 内でのみ一意であることを保証します。異なる doc_id 値は、同じ id を共有できます。ワークロードで id がテーブル全体でグローバルに一意であることが必要な場合は、生成された id がこの要件を満たすことも確実にする必要があります。
分散テーブル間に外部キーを作成する場合、通常、両方のテーブルが同じコロケーショングループにあり、対応する分散列が外部キーの両側に含まれている必要があります。集中型 PolarDB for PostgreSQL データベースからテーブルスキーマを移行する場合、すべての外部キーと、レコードが更新または削除されたときに関連レコードを自動的に変更するすべてのルールを確認してください。特に、これらのルールを介して分散列を変更することは避けてください。
頻繁なトランザクションを単一の分散列の値内に維持
トランザクションが、同じ併置グループの各テーブルで 1 つの doc_id 値のデータのみを変更する場合、関与する DN の数が少なくなります。 トランザクションが複数のシャードにアクセスする必要がある場合は、スループットと応答時間を個別に負荷テストし、同時読み取りおよび書き込み、トランザクションのコミット、ロールバックがワークロード要件を満たしていることを確認してください。 分散テーブルの DML 実行の制限と構文については、「分散テーブルでの DML 操作」をご参照ください。
シーケンスとトリガーの処理
自動インクリメント列への 64 ビットシーケンスの使用
シーケンスと自動インクリメント列の型は、テーブルを作成して分散する前に決定する必要があります。トリガーは個別に評価する必要があります。
自動インクリメント列には
BIGSERIALを使用します。GENERATED ALWAYS AS IDENTITYまたはGENERATED BY DEFAULT AS IDENTITYで定義される ID 列は使用しません。既存のテーブルで ID 列が使用されている場合は、テーブルを分散する前に、列を
BIGSERIALシーケンスベースの自動インクリメント列に変更してください。そうしないと、分散テーブルのスキーマを変更する後続の操作が失敗する可能性があります。シーケンスを明示的に作成する場合は、シーケンスとその値を受け取る列の両方に、分散ワークロードに適した 64 ビット型を使用するようにしてください。
PolarDB for PostgreSQL Distributed Edition は、異なるノードに異なるシーケンス値の範囲を割り当てるため、複数のノードで競合しない 64 ビットのシーケンス値を生成できます。したがって、アプリケーションはシーケンス値が連続していると想定すべきではなく、異なるノードで生成されたシーケンス値の相対的なサイズを使用してレコードが作成された順序を決定すべきではありません。
SMALLSERIAL および SERIAL に関連付けられているシーケンスなど、16 ビットおよび 32 ビットのシーケンスは、プライマリ CN 上でのみ値を生成できます。アプリケーションが分散プロキシエンドポイントを介して書き込みを行う場合、別の CN にルーティングされ、これらのシーケンス値のいずれかを生成する必要があるリクエストは、エラーを返す可能性があります。本番稼働前に、すべてのデフォルト値と明示的な nextval() 呼び出しを確認してください。シーケンスの制限と使用方法については、「分散シーケンスの使用方法と制限」をご参照ください。
BIGSERIAL を使用すると、複数のノードで競合しない自動インクリメント値を生成できますが、INSERT ... SELECT が常に DN 上で直接完了することは保証されません。nextval() またはシーケンスのデフォルト値を含むステートメントについては、ターゲットバージョンで EXPLAIN を使用して実行計画を表示し、クエリ結果を処理のために CN に送り返す必要があるかを確認します。
トリガー
トリガーは、通常、分散ワークロードには推奨されません。既存のワークロードを移行する際は、各トリガーが正しく実行できるか、どのノードで実行されるか、他のテーブルにアクセスする必要があるか、そして複数のリクエストが同時に実行されたときに結果が正しいかどうかを確認してください。
テーブルの作成、権限の付与、データの分散
次の例では、「ビジネスシナリオ」で定義されたドキュメントのワークロードモデルを実装します。まず、テーブルスキーマを作成し、アプリケーションアカウントに権限を付与してから、テーブルを分散テーブルとレプリケーションテーブルに変換します。この例では、新しい空のテーブルを作成してから分散するワークフローについて説明します。すでにデータが含まれているテーブルの移行については、このトピックでは扱いません。
通常の PostgreSQL テーブルの作成
最初にテーブルスキーマを作成します。 自動インクリメント列には BIGSERIAL を使用します。 リビジョンレコードテーブルの主キーには、分散列 doc_id が含まれます。
CREATE TABLE documents (
doc_id BIGSERIAL PRIMARY KEY,
title TEXT NOT NULL,
status_id INTEGER NOT NULL
);
CREATE TABLE document_revisions (
doc_id BIGINT NOT NULL,
id BIGSERIAL NOT NULL,
content TEXT NOT NULL,
PRIMARY KEY (doc_id, id)
);
CREATE TABLE document_status (
status_id INTEGER PRIMARY KEY,
name TEXT NOT NULL
);この例では外部キーを作成しません。ワークロードでデータベースに参照整合性を強制する必要がある場合は、前述の要件に従って制約を設計し、それらを検証してから追加してください。
アプリケーションアカウントへの権限の付与
ワークロードに必要な権限を付与します。 次の例では、app_user に、ドキュメントとリビジョンレコードの読み取りと書き込み、ステータスディクショナリの読み取りのみ、および自動インクリメント列に関連付けられたシーケンスの使用が許可されます。
GRANT CONNECT ON DATABASE appdb TO app_user;
GRANT USAGE ON SCHEMA public TO app_user;
GRANT SELECT, INSERT, UPDATE, DELETE
ON TABLE documents, document_revisions TO app_user;
GRANT SELECT ON TABLE document_status TO app_user;
GRANT USAGE ON SEQUENCE documents_doc_id_seq,
document_revisions_id_seq TO app_user;テーブル権限とシーケンス権限は独立しています。INSERT 権限のみを付与するだけでは、アカウントがシーケンスを呼び出してデフォルト値を生成できるということにはなりません。これらの権限は、記載されているオブジェクトにのみ適用されます。後でテーブルまたはシーケンスを追加する場合は、アプリケーションアカウントの権限を再度確認してください。
テーブルの分散テーブルおよびレプリケーションテーブルへの変換
次の例では、最初にドキュメントテーブル用に独立したコロケーショングループを作成し、次に改訂履歴テーブルをそのグループに追加して、他のワークロードのテーブルとは別に管理されるようにします。最後に、ステータス辞書をレプリケーションテーブルに変換します。
SELECT create_distributed_table(
'documents', 'doc_id', colocate_with := 'none'
);
SELECT create_distributed_table(
'document_revisions', 'doc_id', colocate_with := 'documents'
);
SELECT create_reference_table('document_status');colocate_with を指定せずに分散テーブルを作成すると、デフォルトでは、同じ分散キー列のデータ型と同じシャード数 (shard_count) を持つテーブルが同じコロケーショングループに参加します。テキスト型の分散キー列の場合、照合順序も一致する必要があります。シャード数を指定しない場合、polar_cluster.shard_count の値が使用されます。デフォルト値は 32 で、これは評価の開始点として適しています。ワークロードごとにコロケーショングループを分離するには、colocate_with := 'none' を使用して独立したコロケーショングループを作成し、次に colocate_with := 'table_name' を使用して関連テーブルをそのグループに追加します。テーブルタイプ、コロケーショングループ、および DDL 操作の詳細については、「分散テーブルの作成と管理」をご参照ください。
変換後、次のクエリを実行して、テーブルタイプ、分散列、シャード数、およびコロケーショングループを確認してください。
SELECT table_name, polar_cluster_table_type, distribution_column,
shard_count, colocation_id
FROM polar_cluster_tables
WHERE table_name IN (
'documents'::regclass,
'document_revisions'::regclass,
'document_status'::regclass
)
ORDER BY table_name::text;documents と document_revisions のテーブルタイプが distributed、分散列が doc_id、シャード数が同じ、colocation_id が同じであることを確認します。document_status のテーブルタイプが reference であることを確認します。
分散列の条件を含む SQL の記述
次のクエリは、WHERE d.doc_id = 1001 を使用してドキュメントを指定し、r.doc_id = d.doc_id を使用して同じドキュメントのリビジョンレコードを結合するため、ドキュメントを格納する DN 上で完了できます。
SELECT d.doc_id, d.title, r.id, r.content, s.name AS status_name
FROM documents AS d
JOIN document_revisions AS r ON r.doc_id = d.doc_id
JOIN document_status AS s ON s.status_id = d.status_id
WHERE d.doc_id = 1001;更新と削除も、対象テーブルの分散列の条件を直接指定する必要があります。以下のステートメントは、条件の記述方法を説明するものです。実行する前に、値をワークロードが必要とするレコードに置き換えてください。
UPDATE document_revisions
SET content = 'Updated content'
WHERE doc_id = 1001 AND id = 2001;
DELETE FROM document_revisions
WHERE doc_id = 1001 AND id = 2001;ワークロードの検証と本番稼働
テーブルスキーマの設計と分散が完了したら、標準アプリケーションアカウントを使用して分散プロキシエンドポイント経由で接続し、特権アカウントのみでテストするのではなく、完全なアプリケーションワークフローをテストしてください。
チェック項目 | 検証内容 |
接続性と権限 | ホワイトリスト、エンドポイント、データベース名が正しく、テーブルとシーケンスの権限が要件を満たしていることを確認します。 |
複数 CN での書き込み | 複数の CN がリクエストを受信している間に、挿入、更新、削除、クエリを検証します。特に、シーケンスのデフォルト値に依存する挿入を検証します。 |
単一シャードへのルーティング |
|
結合と一括書き込み |
|
制約とトランザクション | 一意性制約、外部キー、トランザクションのロールバックを検証し、操作が複数のシャードにアクセスする際に読み取りと書き込みの結果がワークロードの要件を満たしていることを確認します。 |
ホットスポットとキャパシティ | 本番のデータ量、少量のデータに集中するリクエスト、本番の同時実行性をシミュレートします。CN と DN のリソース使用率と応答時間を観察します。 |
たとえば、まず更新を実行しない EXPLAIN を使用して、次の更新が doc_id = 1001 を含むシャードのみにアクセスするかどうかを確認します。
EXPLAIN (VERBOSE)
UPDATE document_revisions
SET content = 'Updated content'
WHERE doc_id = 1001 AND id = 2001;実際の測定が必要な場合は、ステートメントを実行する EXPLAIN ANALYZE コマンドをテスト環境で使用します。クエリの最適化方法については、「分散テーブルでのクエリの最適化」をご参照ください。アプリケーションテストと負荷テストが期待値を満たしたら、分散プロキシエンドポイントを介して本番トラフィックを処理できます。
仕様の調整とクラスターのスケーリング
仕様の調整:CN と DN は個別にスケールアップまたはスケールダウンできます。変更を行う前に、「ソリューションアーキテクチャ」で説明されている 2 種類のノードの負荷源に基づいて実際の負荷を評価し、調整するノードタイプを決定してください。スケールダウンする前に、対象の仕様がピーク時のワークロードを処理でき、必要なリソースの余裕を確保できることを確認してください。
ノードの追加または削除:
CN の追加:デフォルトの分散プロキシエンドポイントには、新規追加された CN が含まれます。カスタムエンドポイントが新規追加された CN を自動的に含めるかどうかは、設定時に選択したオプションによって異なります。スケールアウト後、各エンドポイント設定に含まれる実際のノードセットを検証してください。
DN の追加:新しいノードは既存のシャードを自動的に移行しません。新しい DN が既存のデータと関連する負荷を担えるように、シャードリバランスを実行してください。
DN の削除:スケールイン中、コントロールプレーンは自動的にリバランスを実行し、削除されるノードからのシャード移行を完了します。
CN の削除:残りの CN が接続とクエリの負荷を処理できるかどうかを評価してください。
シャードリバランスの手順と、タスクのステータスと進捗を表示する方法は、このトピックでは扱いません。マルチテナントのワークロードでは、「マルチテナント管理」および「テナント分離」を使用して、各テナントのデータをどのノードに格納するかを計画することもできます。
次のステップ
「分散テーブルの作成と管理」を読み、分散テーブル、レプリケーションテーブル、コロケーショングループ、DDL 操作について学びます。
「分散テーブルでの DML 操作」を読み、分散テーブルの DML 実行制限と構文について学びます。
「分散テーブルでのクエリの最適化」を読み、ルーティング、結合、実行計画を確認します。
「分散シーケンスの使用方法と制限事項」を読み、シーケンスと自動インクリメント列を計画します。