Amazon Web Services ブログ

論理レプリケーションを使用してオンプレミスまたは Amazon EC2 から Amazon RDS に PostgreSQL を移行する

PostgreSQL は、オープンソースのリレーショナルデータベースの中でも最も人気のある先進的なシステムの 1 つです。30 年以上におよぶ開発作業を経ている PostgreSQL は、多数の複雑なデータワークロードを処理できる、信頼性が高く堅牢なデータベースであることが証明されています。PostgreSQL は、Oracle や Microsoft SQL Server などの商用データベースから移行する際に多くの人から選ばれるオープンソースデータベースです。クラウドの観点から、AWS は 2 つのマネージド PostgreSQL オプションを提供しています。それは、Amazon Relational Database Service (RDS) for PostgreSQLAmazon Aurora for PostgreSQL です。

PostgreSQL データベースをオンプレミスから AWS マネージド PostgreSQL に移行またはアップグレードする場合、または AWS マネージドサービス内の PostgreSQL のメジャーバージョンにアップグレードする場合は、論理レプリケーションを使用して、ネイティブの PostgreSQL 機能を介して実行できます。pglogical 拡張機能は、バージョン 9.6.6 以降の Amazon RDS for PostgreSQL の一部で、コミュニティ PostgreSQL バージョン 9.4 以降で機能します。 pglogical 拡張機能は、バージョン 9.4 の環境から 9.6.6 以降の環境に移行する場合に適したオプションです。PostgreSQL は、バージョン 10 の組み込み機能としてネイティブ論理レプリケーションも導入しましたが、この記事では取り上げません。

AWS Database Migration Service (DMS) を使用すると、最小限のダウンタイムでオンプレミスの PostgreSQL を Amazon RDS for PostgreSQL に移行できます。AWS DMS には、セットアッププロセスが簡単など、いくつかの利点があります。また、プライマリキーを持たないテーブルを移行し、ソースデータベースとターゲットデータベース間でデータを検証することもできます。ただし、pglogical が克服する必要がある 制限 もいくつかあります。

pglogical は、次のシナリオで役立ちます。

  • テーブルでより多くの bytea、jsonb、および enum データ型を使用する場合
  • シーケンスをレプリケートする場合 (ターゲット DB でのシーケンスの自動インクリメント)
  • データが切り捨てられてテーブルに頻繁に読み込まれる場合
  • 独自のレプリケーションセットを作成する場合 (たとえば、ターゲットデータベースへの挿入と更新のみをレプリケートする場合)。これは AWS DMS の制限です

この記事では、論理レプリケーション (pglogical 拡張を使用)、pglogical のユースケース、およびその制限について説明します。

論理レプリケーションの概要

論理レプリケーションは、レプリケーション ID (通常はプライマリキー) に基づいてデータオブジェクトとその変更をレプリケートする方法です。「論理」という用語は、正確なブロックアドレスとバイト単位のレプリケーションを使用する「物理」レプリケーションの対義語です。論理レプリケーションを使用すると、データベースからターゲットデータベースに変更をリアルタイムでストリーミングできます。

物理レプリケーションと論理レプリケーションの比較

物理レプリケーションは、バイナリ形式でレプリカにデータを送信します。バイナリレプリケーションは、全か無かの方法でクラスタ全体をレプリケーションします。バイナリレプリケーションを使って特定のテーブルまたはデータベースを取得する方法はありません。これは完全なクラスターレベルおよびインスタンスレベルのレプリケーションです。

論理レプリケーションは、データを論理形式でレプリカに送信します。さらに、論理レプリケーションは、1 つのテーブル、データベース、またはテーブル内の特定の列のデータを送信できます。

次の図は、論理レプリケーションのアーキテクチャを示しています

アーキテクチャには次の特長を備えています。

  • pglogical は、物理ストリーミングレプリケーションのようにサーバーレベル全体ではなく、データベースレベルで機能します
  • プロバイダーは、追加のディスク書き込みオーバーヘッドを発生させることなく、複数のサブスクライバーに変更を送信できます
  • あるサブスクライバーは、複数のデータベースからの変更を受け入れ、自動的に設定できる競合解決により変更間の競合を検出できます
  • カスケードレプリケーションは、変更セットの転送の形式で実装されます

論理レプリケーションの仕組み

論理レプリケーションは、1 つ以上のサブスクライバーを備えたパブリッシュおよびサブスクライブメカニズムを使用します。これは、パブリッシャーノード上の 1 つ以上のパブリケーションにサブスクライブします。サブスクライバーはパブリケーションからデータをプルし、その後データを再パブリッシュして、カスケードレプリケーションまたはより複雑な設定が行えるようにします。

マスターノード (ソースデータベース) はパブリケーションを定義します。そのノードが publisher です。パブリッシャーは常に変更データ (DML) をサブスクライバーに送信し、データを複数のサブスクライバーに送信できます。

レプリカサーバー (ターゲットデータベース) はサブスクリプションを定義します。そのノードが subscriber です。サブスクライバーは、複数のパブリッシャーからのデータを受け入れ、変更をターゲットデータベースに適用します。

pglogical は、論理デコードを使用して、データの変更点のみを非同期でレプリケートします。レプリケートされるのは違いのみであるため、これは非常に効率的です。また、障害の後で再開できるため、ネットワーク障害にも耐性があります。

Amazon RDS for PostgreSQL 9.6 以降のバージョンには、pglogical が含まれています。これを使用して、マスターノード (パブリッシャー) とレプリカノード (サブスクライバー) 間の論理レプリケーションをセットアップできます。

次のシナリオで論理レプリケーションを使用できます。

  • 単一のデータベースまたはデータベースのサブセットの増分変更が発生したときにサブスクライバーに送信する
  • 複数のデータベースを単一のデータベースに統合する (たとえば、分析目的で)
  • 複数のデータベース間でデータベースのサブセットを共有する
  • あるメジャーバージョンから別のメジャーバージョンへデータベースをアップグレードする
  • あるプラットフォームから別のプラットフォームへ移行する (オンプレミスまたは Amazon EC2 から Amazon RDS など)

pglogical の使用

この記事では、複数のデータベース (複数のパブリッシャー) から Amazon RDS for PostgreSQL (シングルスタンバイ) への論理レプリケーションをセットアップする方法を示します。この記事では、2 つの EC2 インスタンスを使用します。これらのインスタンス上のコミュニティ PostgreSQL には pglogical 拡張機能がインストールされています。Amazon RDS for PostgreSQL にはデフォルトで pglogical 拡張機能があります。これは、バージョン 9.6 以降に pglogical サポートが含まれているためですが、コミュニティ PostgreSQL の場合、この拡張機能を個別にインストールして設定する必要があります。

この記事では、単一の RHEL-7 EC2 インスタンス (PostgreSQL 9.6 および PostgreSQL 10) で実行される 2 つの PostgreSQL メジャーバージョンを使用しています。

前提条件

始める前に、両方のバージョンの PostgreSQL の postgresql.conf にリストされているパラメータを設定します。ソースデータベースとターゲットデータベースの pg_hba.conf ファイルでレプリケーションユーザーがパラメータ rds.logical_replication を有効にできるようにします。

ソースデータベースの設定

ソースデータベースを設定するには、次の手順を実行します。

  1. ソースデータベースの postgresql.conf で、次のパラメータを編集します。
    wal_level = 'logical'
    track_commit_timestamp = on
    max_worker_processes = 10
    max_replication_slots = 10 (Set as per the requirment looking at publisher or subscriber)
    max_wal_senders = 10
    shared_preload_libraries = 'pglogical'
  2. 最初のソースで、PostgreSQL インスタンスを再起動してこれらのパラメータを有効にします。
  3. 設定されたパラメータを表示するには、次のコードを入力してください。
    /usr/pgsql-9.6/bin/psql -d source1 -p 5432 -c "select name, setting from pg_settings where name in ('wal_level','track_commit_timestamp','max_worker_processes','max_replication_slots','max_wal_senders  ','shared_preload_libraries');" 

    次のスクリーンショットは、このクエリの出力を示します。

  4. 2 番目のソースで、PostgreSQL インスタンスを再起動してこれらのパラメータを有効にします。
  5. 設定されたパラメータを表示するには、次のコードを入力してください。
    /usr/pgsql-10/bin/psql -d source2 -p 5433 -c "select name, setting from pg_settings where name in ('wal_level','track_commit_timestamp','max_worker_processes','max_replication_slots','max_wal_senders  ','shared_preload_libraries');”         

    次のスクリーンショットは、このクエリの出力を示します。

  6. pg_hba.conf で、pg_hba.conf のレプリケーションユーザーが PostgreSQL インスタンスに接続できるようにします。
  7. PostgreSQL インスタンスをリロードします。
    pg_hba.conf で次の変更を確認できます。

    host     replication     all      <RDS endpoint>/32           md5
  8. PostgreSQL インスタンス (source-1source-2) をリロードします。次のコードを参照してください。
    /usr/pgsql-9.6/bin/psql -d source1 -p 5432 -c "select pg_reload_conf();"  
    /usr/pgsql-10/bin/psql  -d source2 -p 5433 -c "select pg_reload_conf();" 

ターゲット DB パラメータグループの設定

Amazon RDS for PostgreSQL には、デフォルトで pglogical 拡張子が付属しています。ターゲット DB パラメータグループを設定するには、次の手順を実行します。

  1. 拡張機能を有効にするには、ターゲットデータベースで、データベースのパラメータグループrds.logical_replication = 1 および shared_preload_libraries = 'pglogical' を設定します。
  2. これらのパラメータを有効にするには、RDS インスタンスを再起動します。
  3. パラメータがターゲットデータベースに設定されているかどうかを確認するには、次のコードを入力します。
    psql -h <your_rds_endpoint> -d target -U postgres -W -c "select name, setting from pg_settings where name in ('wal_level','track_commit_timestamp','max_worker_processes','max_replication_slots','max_wal_senders  ','shared_preload_libraries');"

    次のスクリーンショットは、このクエリの出力を示します。
    Amazon RDS では、rds.logical_replication を有効にするたびに、レプリケーションエントリが pg_hba ルールに追加されます。これを確認するには、次のコードを入力します。

    ./psql -h <your_rds_endpoint> -d target -U postgres -c "select pg_hba_file_rules();

    次のスクリーンショットは、ターゲットデータベースのレプリケーションユーザーのための pg_hba.conf のルールを示しています。

論理レプリケーションのセットアップ

これで、EC2 ホスト上の複数のデータベース間の論理レプリケーションを設定する準備ができました。次の手順を実行します。

  1. pglogical rpm をダウンロードして、ソースデータベースにインストールします。
    PostgreSQL 9.6 の場合、次のコードを入力します。

    Curl https://access.2ndquadrant.com/api/repository/dl/default/release/9.6/rpm | bash
    yum install postgresql96-pglogical

    PostgreSQL 10 の場合、次のコードを入力します。

    curl https://access.2ndquadrant.com/api/repository/dl/default/release/10/rpm | bash
    yum install postgresql10-pglogical

    これで、ソースデータベースとターゲットデータベースに pglogical 拡張を作成できます。

  2. ソースデータベースで、次のコードを使用して pglogical 拡張を作成します。
    /usr/pgsql-9.6/bin/psql -d source1 -p 5432 -c "create extension pglogical;"
    /usr/pgsql-10/bin/psql -d source2 -p 5433 -c "create extension pglogical;"

    次のスクリーンショットは、pglogical's バージョンがソースデータベースにあることを示しています。
    pglogical スキーマおよびその他のオブジェクトは、pglogical スキーマの下に作成されます。これは、レプリケーションの情報を維持するのに役立ちます。
    次のコードは、ターゲットデータベースに pglogical 拡張を作成します。

    psql -h <your_rds_endpoint> -d target -U postgres -W -c "create extension pglogical;"

    次のコードは、pglogical がターゲットデータベースにあることを示します。

    psql -h <your_rds_endpoint> -d target -U postgres -W  -c "SELECT e.extname AS "Name", e.extversion AS "Version", n.nspname AS "Schema", c.description AS "Description" FROM pg_catalog.pg_extension e LEFT JOIN pg_catalog.pg_namespace n ON n.oid = e.extnamespace LEFT JOIN pg_catalog.pg_description c ON c.objoid = e.oid AND c.classoid = 'pg_catalog.pg_extension'::pg_catalog.regclass where e.extname='pglogical' ORDER BY 1;"

    次のスクリーンショットは、pglogical がターゲットデータベースにあることを示しています。

  3. ソースデータベースにパブリケーションサーバーを作成し、テーブルとシーケンスをレプリケーションセットに追加します (ポイント 8 で説明)。
  4. PostgreSQL 9.6 の場合、次のコードで source-1 のテーブルとシーケンスのリストを確認します。
    /usr/pgsql-9.6/bin/psql -d source1 -p 5432 <<EOF
    \dt \ds
    EOF

    次のスクリーンショットは、source-1 データベースに存在するシーケンスとテーブルのリストを示しています。

  5. PostgreSQL 10 の場合、次のコードで source-2 のテーブルとシーケンスのリストを確認します。
    /usr/pgsql-10/bin/psql -d source2 -p 5433 <<EOF
    \dt \ds
    EOF

    次のスクリーンショットは、source-2 データベースに存在するシーケンスとテーブルのリストを示しています。

  6. パブリケーションサーバーを作成する前に、PostgreSQL (バージョン 9.6 と 10) からスキーマダンプを取得し、それをターゲットの Amazon RDS for PostgreSQL に復元します。ダンプを使用してこれを行うことができ、ネイティブの PostgreSQL ユーティリティを使用して復元できます (pgdump -s がダンプを取得し、psql -f<file> が復元する)。
  7. 次のコードを使用して、ソースデータベースにパブリッシャーを作成します。
    /usr/pgsql-9.6/bin/psql -d source1 -p 5432 <<EOF
    SELECT pglogical.create_node( node_name := 'provider1', dsn := 'host=<your_ec2_endpoint> port=5432 dbname=source1 user=postgres' );
    EOF
    
    /usr/pgsql-10/bin/psql -d source2 -p 5433 <<EOF
    SELECT pglogical.create_node( node_name := 'provider2', dsn := 'host=<your_ec2_endpoint> port=5433 dbname=source2 user=postgres' );
    EOF

    次のスクリーンショットは、source-1 および source-2 データベースでのパブリッシャーノードの作成を示しています。
    次の pglogical.node_interface テーブルのスクリーンショットは、追加されたノードのエントリを示しています。

  8. テーブルとシーケンスをデフォルトのレプリケーションセットに追加します。次のコードを参照してください。
    /usr/pgsql-9.6/bin/psql -d source1 -p 5432 <<EOF
    SELECT pglogical.replication_set_add_all_tables('default', '{public}'::text[],synchronize_data := true);
    select pglogical.replication_set_add_all_sequences( set_name := 'default', schema_names := '{public}'::text[], synchronize_data := true );
    EOF
    
    /usr/pgsql-10/bin/psql -d source2 -p 5433 <<EOF
    SELECT pglogical.replication_set_add_all_tables('default', '{public}'::text[],synchronize_data := true);
    select pglogical.replication_set_add_all_sequences( set_name := 'default', schema_names := '{public}'::text[], synchronize_data := true );
    EOF

    次のスクリーンショットは、source-1 および source-2 データベースのレプリケーションセットへシーケンスとテーブルを追加したことを示しています。
    前のスクリーンショットでは、レプリケーションセットにシーケンスが追加されています。これにより、ターゲットのシーケンスカウンターが増加します。
    これで、ソースデータベース (バージョン 9.6 と 10) からスキーマダンプを取得して、それをターゲットの Amazon RDS for PostgreSQL データベースに復元し、ターゲットデータベースにサブスクリプションサーバーを作成できます。

  9. ターゲットデータベースのテーブルとシーケンスを一覧表示するには、次のコードを入力します。
    psql -h <your_rds_endpoint> -d target -U postgres -W <<EOF
    \dt \ds
    EOF

    次のスクリーンショットは、ターゲットデータベースに存在するシーケンスとテーブルを一覧表示したものです。

  10. ターゲットデータベースに 1 つのサブスクライバーと 2 つのサブスクリプションを作成します。次のコードを参照してください。
    psql -h <your_rds_endpoint> -d target -U postgres  <<EOF
    SELECT pglogical.create_node(
        node_name := 'subscriber1',
        dsn := 'host=<your_rds_endpoint> port=5432 dbname=target password=postgres user=postgres'
    );
    EOF

    次のスクリーンショットは、ターゲットデータベースに作成されたサブスクライバーを示しています。

  11. 次のコードを使用して、source-1source-2 のターゲットデータベースにサブスクリプションを作成します。
    psql -h <your_rds_endpoint> -d target -U postgres  <<EOF
    SELECT pglogical.create_subscription(
        subscription_name := 'subscription1',
        replication_sets := array['default'],
        provider_dsn := 'host=<your_ec2_endpoint> port=5432 dbname=source1 password=postgres user=postgres'
    );
    
    SELECT pglogical.create_subscription(
        subscription_name := 'subscription2',
        replication_sets := array['default'],
        provider_dsn := 'host=<your_ec2_endpoint> port=5433 dbname=source2 password=postgres user=postgres'
    );
    EOF

    次のスクリーンショットは、ターゲットデータベースで作成されたサブスクリプションを示しています。

  12. データをソースインスタンスにロードする前に、ソース PostgreSQL (9.6、10) とターゲット RDS PostgreSQL 11 のテーブルの行数を比較します。
    次のスクリーンショットは、ソースデータベースとターゲットデータベースのテーブルの行数の比較を示しています。
    次のスクリーンショットは、挿入ステートメントを含む小さな for ループスクリプトを示しています。これにより、データをソースデータベースにロードします。
    次のスクリーンショットは、データのロード前後のソースデータベースとターゲットデータベース間のテーブル数を比較しています。
    テーブルデータはソースデータベースとターゲットデータベース間でレプリケートされますが、シーケンスについてはどうでしょうか? レプリケーションセットに追加されたシーケンスの状態は、リアルタイムではなく定期的にレプリケートされます。
    次のスクリーンショットは、シーケンスがレプリケーションセットに追加された場合の、ソースデータベースとターゲットデータベースでのシーケンスの動作を示しています。
    シーケンスを使用して初期データをロードしている間に、シーケンス値はソースからターゲットデータベースにすぐにレプリケートされます。ただし、後のロードサイクル中に、シーケンスデータをレプリケートするのに時間がかかる場合があります。前述のように、シーケンスの同期は連続的でもリアルタイムでもありません。pglogical は、ターゲットデータベースのシーケンスを定期的に同期します。
    次のスクリーンショットは、ソースデータベースとターゲットデータベースのシーケンスの増分値を示しています。
    この記事では、pglogical がシーケンスをターゲットデータベースにレプリケートするのに時間がかかりました (3 分以上)。
  13. レプリカに切り替えるときに、pglogical.synchronize_sequence 関数を実行して、ターゲットデータベースのすべてのシーケンスを同期します。現在のプライマリへのアプリケーション接続が切断された後に、これを実行する必要があります。次のコードを参照してください。
    /usr/pgsql-9.6/bin/psql -d source1 -p 5432 <<EOF
    select pglogical.synchronize_sequence( seqoid ) from pglogical.sequence_state;
    EOF
    /usr/pgsql-9.6/bin/psql -d source2 -p 5433 <<EOF
    select pglogical.synchronize_sequence( seqoid ) from pglogical.sequence_state;
    EOF

    次のスクリーンショットは、前述のコードを入力した後のソースデータベースとターゲットデータベースでのシーケンスの同期を示しています。
    動的バッファーは、レプリケートされる値に使用します。これにより、サブスクライバーがシーケンスの将来の状態を受信できるようにします。こうすることで、サブスクライバーのシーケンスの last_value が遅れる可能性を最小限に抑えられますが、可能性を完全に排除するわけではありません。synchronize_sequence を呼び出して、データベースの重要なイベント (データの読み込み中やオンラインアップグレード中など) の後で、すべてのサブスクライバーが特定のシーケンスに関する最新の情報を持っていることを確認できます。

pglogical の制限

pglogical の使用には、次の制限があります。

  • これを書いている時点では、pglogical のレプリケーションと管理にはスーパーユーザー権限が必要です。その後、より細かい権限に拡張される可能性があります。
  • UNLOGGED テーブルと TEMPORARY テーブルは、物理ストリーミングレプリケーションと同じ様に、レプリケートされず、さらにレプリケートすることもできません。これは、これらのテーブルの DML が WAL ログ (トランザクションログ) にエントリを持たず、論理ワーカープロセスがこれらのテーブルで発生する変更をキャプチャすることが難しいためです。
  • 複数のデータベースをレプリケートする場合は、それぞれに個別のプロバイダーとサブスクライバーの関係をセットアップする必要があります。PostgreSQL インストールのすべてのデータベースのレプリケーションを一度に設定することはできません。
  • プライマリまたはその他の有効なレプリカ ID がないテーブルに対しては、UPDATE および DELETE 関数をレプリケートすることはできません。たとえば、UNIQUE レプリケーションは、一意の識別子がないため、更新または削除するテーブルを見つけることができません。Bucardo や slony などのトリガーベースのツールを含む、市場に出回っている他のツールと同様に、pglogical では、DML をキャプチャするためにテーブルにプライマリキーまたは一意のキーが必要です。
  • プロバイダーからサブスクライバーに DDL を自動的にレプリケートする方法はありません。pglogical は、pglogical.replicate_ddl_command 関数を提供して、DDL をプロバイダーとサブスクライバーで一貫したポイントで実行できるようにします。たとえば、[pglogical.replicate_ddl_command('alter table public.migration1 add column surname varchar(20)');] を選択します。
  • TRUNCATE...CASCADE を使用すると、プロバイダー側の CASCADE オプションのみが適用されます。

まとめ

この記事では、論理レプリケーションと物理レプリケーションの違い、ユースケース、論理レプリケーションの利点、および pglogical 拡張機能と組み込みの論理レプリケーションを使用して、PostgreSQL の異なるバージョン間で論理レプリケーションを実装する方法について説明しました。論理レプリケーションは、標準のストリーミングレプリケーションメカニズムよりも実行しやすく簡単な方法でテーブルをレプリケートします。ただし、いくつかの制限があり、この制限がレプリケーションに使用するかどうかの決定に影響するかもしれません。

 


著者について

 

Rajesh Madiwale は、アマゾン ウェブ サービスのアソシエイトコンサルタントです。彼は、Amazon RDS for PostgreSQL、Aurora PostgreSQL、Redshift、MySQL、Greenplum データベースでのデータベース開発と管理に関する深い専門知識を持っています。彼は PostgreSQL コミュニティの熱心なメンバーであり、任期を通して PostgreSQL に取り組んできました。彼はまた、PostgreSQL カンファレンスの複数のセッションで講演しました。