Amazon Web Services ブログ

Amazon RDS for PostgreSQL で高速リフレッシュ機能を構築する

この記事は CDL によるゲスト投稿です。CDL は自社を次のように紹介しています。「CDL は英国を拠点とする主要なインシュアテック企業であり、Financial Times 誌の業界に影響を与えている高成長の英国企業 Future 100 のリストに掲載されています。保険と金融サービスの分野で強力な実績を残し、そのソリューションは英国で最も収益性の高い保険代理店を支えています。 CDL はシステム上で 700 万件を超える保険契約を取引し、Sainsbury’s Bank、Tesco Bank、Swinton Insurance、Moneysupermarket.com などをクライアントに抱えています」

この記事では、CDL が Amazon RDS for PostgreSQL のマテリアライズドビューログをどう使って高速リフレッシュ機能を開発したかについて説明します。変更を追跡するために構築したものを詳述し、完全リフレッシュに代わる代替手段を示します。これにより、必要な時間が数時間から数秒に短縮されました。また、高速リフレッシュを可能にするためのオープンソースソフトウェアをより広い PostgreSQL コミュニティと共有し、関連するインストールプロセスの概要を示します。

課題

CDL は毎日数百万のトランザクションを処理しています。当時はビジネスインテリジェンス (BI) プラットフォームを Oracle から PostgreSQL に移行することを検討していました。切り替えを実際に行うには、お客様が最新のビジネスインテリジェンスへ引き続きアクセスできるようにするため、リレーショナルデータベースでこの大量の変更を処理し、ほぼリアルタイムでリフレッシュする必要がありました。

PostgreSQL は完全リフレッシュ機能のみを備えています。けれども、Google のサービスレベルアグリーメントでは 15 分ごとにデータをリフレッシュする必要があります。そして CDL は大量の変更を処理していることから、完全リフレッシュプロセスではこのタイムスケール内でこの規模のマテリアライズドビュー (MV) を処理することは不可能でした。当社の最大の MV ログは 150 GB を超えており、完全リフレッシュプロセスでは構築するのに何時間もかかります。またお客様によっては、1 日あたりのビュー数が 150 回を超える方もいらっしゃいます。

当社は、BI ソリューションの MV を使用して、オンライントランザクション処理 (OLTP) レイヤーからのデータを論理的にデータマートに非正規化しています。それにより、クライアントがデータを使用してデータ分析を実行できるようにしています。したがって、高速にリフレッシュできることは、Oracle から PostgreSQL に切り替えたときの CDL の重要な前提条件でした。

高速リフレッシュと完全リフレッシュ

高速リフレッシュでは、基になるテーブルのマテリアライズドビューログを使用して変更を追跡し、最後のリフレッシュ以降の変更のみが MV に適用されます。対照的に、完全リフレッシュプロセスはすべてのデータをリフレッシュします。そのため、大規模なデータセットの場合は必然的に数時間かかる可能性があります。

次の図は、ソリューションで MV を使用する方法を示しています。Attunity Replicate を使用して、OLTP レイヤーから RDS インスタンスにデータを同期します。次に、OLTP ソーステーブルから RDS インスタンスに MV を構築し、リフレッシュスケジュールにより 15 分ごとにジョブをリフレッシュします。

PostgreSQL のプロセスの開発

高速リフレッシュプロセスは、MV プロセスの実行に必要な機能を含む独自のスキーマにインストールされるように設計されています。それには、3 つのデータディクショナリテーブルと 1 つのロールがあります。

次の図の MV ログ作成のワークフローは以下のとおりです。

  1. ソーステーブルに UUID 列を追加する
  2. ソーステーブルの MV ログを作成する
  3. MV ログテーブルにインデックスを追加する
  4. ソーステーブルにトリガーを作成する
  5. エントリを pgmview_logs テーブルに挿入する

次の図に示す MV 作成のワークフローは以下のとおりです。

  1. SQL ステートメントを分解する
  2. 複合 MV テーブルを抽出する
  3. MV SQL からベーステーブルを作成する
  4. ソーステーブルに rowid を追加する
  5. rowid 列にインデックスを追加する
  6. pgmviews テーブルに挿入する
  7. pgmviews_details テーブルに挿入する
  8. MV 上で完全リフレッシュを行う

ユーザーが実行する主な機能は次の 4 つです。

  • mv$createMaterializedViewlog – MV ログを作成する
  • mv$createMaterializedView – MV を作成する
  • mv$removeMaterializedViewLog – MV ログを削除する
  • mv$removeMaterializedView – MV を削除する

ソースコード

GitHub リポジトリからソースコードをダウンロードします。次のコードを参照してください。

git clone https://github.com/CDLSoftware/pg-mv-fast-refresh.git

高速リフレッシュ機能のインストール

高速リフレッシュ機能のインストールは、MODULEOWNER パラメータで指定されたデータベース内の独自のスキーマに常駐するように設計されています。

MV コードをインストールするには、次の手順を実行します。

  1. リポジトリをダウンロードしたフォルダに移動し、module_set_variable.sh ファイルを編集します。
    これは、高速リフレッシュ機能をインストールする場所のすべての変数が格納される場所です。
    高速リフレッシュ機能をインストールするのに、SOURCEUSERNAME/SOURCEPASSWORD および MVUSERNAME/MVPASSWORD パラメータは必要ありません。そのようなパラメータはテストハーネスのセットアップに使用します。次のコードを参照してください。

    cd pg-mv-fast-refresh
    vi module_set_variables.sh
    
    MODULEOWNER=<MODULEOWNER> - The module owner username
    MODULE_HOME=<MODULE_HOME> - The Module home path 
    MODULEOWNERPASS=<MODULEOWNERPASS> - Password for module owner PGRS_MVIEW
    HOSTNAME=<HOSTNAME> - Hostname for database
    PORT=<PORT>	 - port for database
    DBNAME=<DBNAME>	 - Database Name
    PGUSERNAME=<PGUSERNAME> - DB username for the module installation run
    PGPASSWORD=<PGPASSWORD> - DB username password for the module installation run
    SOURCEUSERNAME=<SOURCEUSERNAME> - DB username for the source tables for the MV
    SOURCEPASSWORD=<SOURCEPASSWORD> - DB password for the source tables user
    MVUSERNAME=<MVUSERNAME> - DB username for the MV owner
    MVPASSWORD=<MVPASSWORD> - DB password for the MV owner
    LOG_FILE=<LOG_PATH> - Path to logfile output location

    テストケースで使用するパラメータ設定の例として、データベース testpoc およびマスターユーザー名 dbaminを持つ RDS インスタンス pg-tonytest.citnv5igau2v.eu-west-1.rds.amazonaws.com があります。
    インストールパッケージは、スキーマ testpoc の下に高速リフレッシュ機能をインストールします。
    ソーススキーマ testpocsource があります。これは、ソースデータテーブルがテストハーネスと testpocmv のために移動する場所です。後者は、MV が構築されるスキーマです。次のコードを参照してください。

    ort MODULEOWNER=testpoc
    export MODULE_HOME=/var/lib/pgsql/pg-mv-fast-refresh
    export MODULEOWNERPASS=testpoc123
    export HOSTNAME=pg-tonytest.citnv5igau2v.eu-west-1.rds.amazonaws.com
    export PORT=5432
    export DBNAME=testpoc
    export PGUSERNAME=dbadmin
    export PGPASSWORD=testpoc123
    export SOURCEUSERNAME=testpocsource
    export SOURCEPASSWORD=testpoc123
    export MVUSERNAME=testpocmv
    export MVPASSWORD=testpoc123
    export LOG_FILE=/tmp/fast_refresh_module_install_`date +%Y%m%d-%H%M`.log
  2. 実行するスクリプト runCreateFastRefreshModule.sh のアクセス許可を変更して、実行します。次のコードを参照してください。
    chmod 700 runCreateFastRefreshModule.sh
    ./runCreateFastRefreshModule.sh

    このコードの実行には数秒とかからないはずです。

  1. 完了したら、設定した場所にあるログファイルを確認します。
    この記事では、ログファイルは /tmp にあります。ステータスは下部にあります。次のコード例を参照してください。

    -bash-4.1$ cat fast_refresh_module_install_20190913-1358.log
    INFO: Set variables
    INFO: LOG_FILE parameter set to /tmp/fast_refresh_module_install_20190913-1358.log
    INFO: MODULEOWNER parameter set to testpoc
    INFO: PGUSERNAME parameter set to dbadmin
    INFO: HOSTNAME parameter set to pg-tonytest.citnv5igau2v.eu-west-1.rds.amazonaws.com
    INFO: PORT parameter set to 5432
    INFO: DBNAME parameter set to strata
    INFO: MODULE_HOME parameter set to /var/lib/pgsql/pg-mv-fast-refresh
    INFO: Run testpoc schema build script
    INFO: Connect to postgres database strata via PSQL session
    …….. cut lines………..
    GRANT
    INFO: Running Module Deployment Error Checks
    INFO: All Objects compiled successfully
    INFO: No Errors Found
    INFO: Completed Module Deployment Error Checks

データベースの関数を見る

パッケージをインストールしたデータベースに接続すると、MODULEOWNER の下に関数が表示されるはずです。この記事では、関数は testpoc の下にあります。

テストハーネスのインストール

次のユースケースは、モジュールビュー (MV) の作成方法を示しています。6 つのテーブルを作成し、いくつかのデータをテーブルに挿入します。以下のすべての手順は、create_test_harness.sh スクリプトにあり、module_set_variables.sh で設定された変数を使用します。

  1. テストデータ用ののスキーマを作成します。次のコードを参照してください。
    psql --host=$HOSTNAME --port=$PORT --username=$PGUSERNAME --dbname=$DBNAME
    
    CREATE USER $SOURCEUSERNAME WITH
    LOGIN
    NOSUPERUSER
    NOCREATEDB
    NOCREATEROLE
    INHERIT
    NOREPLICATION
    CONNECTION LIMIT -1
    PASSWORD '$SOURCEPASSWORD';
    
    GRANT ALL PRIVILEGES ON DATABASE "$DBNAME" to $SOURCEUSERNAME;
    GRANT $SOURCEUSERNAME to $PGUSERNAME;
    CREATE SCHEMA $SOURCEUSERNAME AUTHORIZATION $SOURCEUSERNAME;
    GRANT ALL PRIVILEGES ON SCHEMA $SOURCEUSERNAME to $PGUSERNAME;
    GRANT $SOURCEUSERNAME to $MODULEOWNER;
    GRANT USAGE ON SCHEMA $SOURCEUSERNAME TO $MODULEOWNER;
    GRANT ALL ON SCHEMA $MODULEOWNER TO $SOURCEUSERNAME;
  2. MV のスキーマを作成します。次のコードを参照してください。
    psql --host=$HOSTNAME --port=$PORT --username=$PGUSERNAME --dbname=$DBNAME
    
    CREATE USER $MVUSERNAME WITH
    LOGIN
    NOSUPERUSER
    NOCREATEDB
    NOCREATEROLE
    INHERIT
    NOREPLICATION
    CONNECTION LIMIT -1
    PASSWORD '$MVPASSWORD';
    
    GRANT $MVUSERNAME to $PGUSERNAME;
    CREATE SCHEMA IF NOT EXISTS $MVUSERNAME AUTHORIZATION $MVUSERNAME;
    GRANT ALL ON SCHEMA $MVUSERNAME TO $MVUSERNAME;
    GRANT SELECT ON ALL TABLES IN SCHEMA $SOURCEUSERNAME TO $MVUSERNAME;
    GRANT pgmv\$_role TO $MVUSERNAME;
    ALTER DATABASE $DBNAME SET SEARCH_PATH=public,$MODULEOWNER,$MVUSERNAME,$SOURCEUSERNAME;
    GRANT $SOURCEUSERNAME TO $MODULEOWNER;
    GRANT USAGE ON SCHEMA $SOURCEUSERNAME TO $MODULEOWNER;
    GRANT ALL PRIVILEGES ON DATABASE strata TO $MODULEOWNER;
    GRANT ALL ON SCHEMA $MVUSERNAME  TO $MODULEOWNER;
    GRANT USAGE ON SCHEMA $MVUSERNAME  TO $MODULEOWNER;
    GRANT $MVUSERNAME  TO $MODULEOWNER;
    GRANT $MODULEOWNER TO $MVUSERNAME;
    GRANT USAGE ON SCHEMA $MODULEOWNER TO $MVUSERNAME;
    GRANT ALL ON SCHEMA $SOURCEUSERNAME TO $MODULEOWNER;
  1. テストデータを sourceusername として作成します。次のコードを参照してください。
    psql --host=$HOSTNAME --port=$PORT --username=$SOURCEUSERNAME --dbname=$DBNAME
    
    -- create t1 table
    
    CREATE TABLE $SOURCEUSERNAME.t1
    (
        id numeric NOT NULL,
        lookup_id numeric,
        code character varying(10) COLLATE pg_catalog."default",
        CONSTRAINT t1_pkey PRIMARY KEY (id)
    );
    
    -- create t2 table
    
    CREATE TABLE $SOURCEUSERNAME.t2
    (
        id numeric NOT NULL,
        description character varying(100) COLLATE pg_catalog."default",
        metavals_id numeric,
        age integer NOT NULL,
        CONSTRAINT t2_pkey PRIMARY KEY (id)
    );
    
    -- create t3 table
    
    CREATE TABLE $SOURCEUSERNAME.t3
    (
        lookup_id numeric NOT NULL,
        lookup_code character varying(10) COLLATE pg_catalog."default" NOT NULL,
        lookup_description character varying(50) COLLATE pg_catalog."default" NOT NULL,
        CONSTRAINT t3_pkey PRIMARY KEY (lookup_id)
    );
    
    -- create t4 table
    
    CREATE TABLE $SOURCEUSERNAME.t4
    (
        metavals_id numeric NOT NULL,
        code character varying(10) COLLATE pg_catalog."default" NOT NULL,
        description character varying(30) COLLATE pg_catalog."default",
        CONSTRAINT t4_pkey PRIMARY KEY (metavals_id)
    );
    
    -- create t5 table
    
    CREATE TABLE $SOURCEUSERNAME.t5
    (
        id numeric NOT NULL,
        rep_ind character varying(1) COLLATE pg_catalog."default",
        trans_id numeric,
        CONSTRAINT t5_pkey PRIMARY KEY (id)
    );
    
    -- create t6 table
    
    CREATE TABLE $SOURCEUSERNAME.t6
    (
        trans_id numeric NOT NULL,
        payment_reference character varying(20) COLLATE pg_catalog."default" NOT NULL,
        CONSTRAINT t6_pkey PRIMARY KEY (trans_id)
    );
    
    -- insert records into t1 table
    
    INSERT INTO $SOURCEUSERNAME.t1(
    	id, lookup_id, code)
    	VALUES (1, 10, 'hello');
    
    INSERT INTO $SOURCEUSERNAME.t1(
    	id, lookup_id, code)
    	VALUES (2, 20, 'bye');
    
    INSERT INTO $SOURCEUSERNAME.t1(
    	id, lookup_id, code)
    	VALUES (3, 30, 'cya');
    
    INSERT INTO $SOURCEUSERNAME.t1(
    	id, lookup_id, code)
    	VALUES (4, 50, 'goodbye');
    
    INSERT INTO $SOURCEUSERNAME.t1(
    	id, lookup_id, code)
    	VALUES (5, 50, 'hi');
    
    INSERT INTO $SOURCEUSERNAME.t1(
    	id, lookup_id, code)
    	VALUES (6, 20, 'bye');
    
    -- insert records into t2 table
    
    INSERT INTO $SOURCEUSERNAME.t2(
    	id, description, metavals_id, age)
    	VALUES (1, 'house', 100, 20);
    
    INSERT INTO $SOURCEUSERNAME.t2(
    	id, description, metavals_id, age)
    	VALUES (2, 'flat', 200, 35);
    
    INSERT INTO $SOURCEUSERNAME.t2(
    	id, description, metavals_id, age)
    	VALUES (3, 'bungalow', 300, 30);
    
    INSERT INTO $SOURCEUSERNAME.t2(
    	id, description, metavals_id, age)
    	VALUES (4, 'palace', 300, 30);
    
    INSERT INTO $SOURCEUSERNAME.t2(
    	id, description, metavals_id, age)
    	VALUES (5, 'office', 400, 50);
    
    -- insert records into t3 table
    
    INSERT INTO $SOURCEUSERNAME.t3(
    	lookup_id, lookup_code, lookup_description)
    	VALUES (10, 'ENG', 'ENGLAND');
    
    INSERT INTO $SOURCEUSERNAME.t3(
    	lookup_id, lookup_code, lookup_description)
    	VALUES (20, 'WAL', 'WALES');
    
    INSERT INTO $SOURCEUSERNAME.t3(
    	lookup_id, lookup_code, lookup_description)
    	VALUES (30, 'SCO', 'SCOTLAND');
    
    INSERT INTO $SOURCEUSERNAME.t3(
    	lookup_id, lookup_code, lookup_description)
    	VALUES (40, 'IRE', 'IRELAND');
    
    INSERT INTO $SOURCEUSERNAME.t3(
    	lookup_id, lookup_code, lookup_description)
    	VALUES (50, 'FRA', 'FRANCE');
    
    -- insert records into t4 table
    
    INSERT INTO $SOURCEUSERNAME.t4(
    	metavals_id, code, description)
    	VALUES (100,'CHAIR','SMALL CHAIR');
    
    INSERT INTO $SOURCEUSERNAME.t4(
    	metavals_id, code, description)
    	VALUES (200,'TABLE','SMALL TABLE');
    
    INSERT INTO $SOURCEUSERNAME.t4(
    	metavals_id, code, description)
    	VALUES (300,'LIGHT','BRIGHT LIGHT');
    
    INSERT INTO $SOURCEUSERNAME.t4(
    	metavals_id, code, description)
    	VALUES (400,'BED','KING SIZE BED');
    
    INSERT INTO $SOURCEUSERNAME.t4(
    	metavals_id, code, description)
    	VALUES (500,'CUPBOARD','BEDSIDE CUPBOARD');
    
    -- insert records into t5 table
    
    INSERT INTO $SOURCEUSERNAME.t5(
    	id, rep_ind, trans_id)
    	VALUES (1, 'Y', 1000);
    
    INSERT INTO $SOURCEUSERNAME.t5(
    	id, rep_ind, trans_id)
    	VALUES (2, 'Y', 2000);
    
    INSERT INTO $SOURCEUSERNAME.t5(
    	id, rep_ind, trans_id)
    	VALUES (3, 'N', 3000);
    
    INSERT INTO $SOURCEUSERNAME.t5(
    	id, rep_ind, trans_id)
    	VALUES (4, 'Y', 4000);
    
    INSERT INTO $SOURCEUSERNAME.t5(
    	id, rep_ind, trans_id)
    	VALUES (5, 'N', 5000);
    
    -- insert records into t6 table
    
    INSERT INTO $SOURCEUSERNAME.t6(
    	trans_id, payment_reference)
    	VALUES (1000, 'GZ-1000');
    
    INSERT INTO $SOURCEUSERNAME.t6(
    	trans_id, payment_reference)
    	VALUES (2000, 'AZ-2000');
    
    INSERT INTO $SOURCEUSERNAME.t6(
    	trans_id, payment_reference)
    	VALUES (3000, 'BZ-3000');
    
    INSERT INTO $SOURCEUSERNAME.t6(
    	trans_id, payment_reference)
    	VALUES (4000, 'QZ-4000');
    
    INSERT INTO $SOURCEUSERNAME.t6(
    	trans_id, payment_reference)
    	VALUES (5000, 'VZ-5000');
  1. MV ログを作成します。次のコードを参照してください。
    psql --host=$HOSTNAME --port=$PORT --username=$SOURCEUSERNAME --dbname=$DBNAME
    
    DO
    \$do\$
    DECLARE
        cResult CHAR(1) := NULL;
    BEGIN
        cResult := $MODULEOWNER.mv\$createMaterializedViewlog( 't1','$SOURCEUSERNAME');
        cResult := $MODULEOWNER.mv\$createMaterializedViewlog( 't2','$SOURCEUSERNAME');
        cResult := $MODULEOWNER.mv\$createMaterializedViewlog( 't3','$SOURCEUSERNAME');
        cResult := $MODULEOWNER.mv\$createMaterializedViewlog( 't4','$SOURCEUSERNAME');
        cResult := $MODULEOWNER.mv\$createMaterializedViewlog( 't5','$SOURCEUSERNAME');
        cResult := $MODULEOWNER.mv\$createMaterializedViewlog( 't6','$SOURCEUSERNAME');
    
    END
    \$do\$;
  1. MV を作成します。次のコードを参照してください。
    psql --host=$HOSTNAME --port=$PORT --username=$MVUSERNAME --dbname=$DBNAME
    
    DO
    \$do\$
    DECLARE
        tStartTime      TIMESTAMP   := clock_timestamp();
        cResult         CHAR(1)     := NULL;
        pSqlStatement   TEXT;
    BEGIN
    
        pSqlStatement := '
    SELECT t1.id t1_id,
    t1.lookup_id t1_lookup_id,
    t1.code t1_code,
    t2.id t2_id,
    t2.description t2_desc,
    t2.metavals_id t2_metavals_id,
    t2.age t2_age,
    t3.lookup_id t3_lookup_id,
    t3.lookup_code t3_lookup_code,
    t3.lookup_description t3_lookup_desc,
    t4.metavals_id t4_metavals_id,
    t4.code t4_code,
    t4.description t4_desc,
    t5.id t5_id,
    t5.rep_ind t5_rep_ind,
    t5.trans_id t5_trans_id,
    t6.trans_id t6_trans_id,
    t6.payment_reference t6_payment_ref
    FROM
    t1
    INNER JOIN t2 ON t1.id = t2.id
    LEFT JOIN t3 ON t1.lookup_id = t3.lookup_id
    LEFT JOIN t4 ON t2.metavals_id = t4.metavals_id
    INNER JOIN t5 ON t1.id = t5.id
    LEFT JOIN t6 ON t5.trans_id = t6.trans_id';
        cResult := mv\$createMaterializedView
        (
            pViewName           => 'mv_fast_refresh_funct_test',
            pSelectStatement    =>  pSqlStatement,
            pOwner              => '$MVUSERNAME',
            pFastRefresh        =>  TRUE
        );
        RAISE NOTICE 'Complex Materialized View creation took % % %', clock_timestamp() - tStartTime, chr(10), chr(10);
    END
    \$do\$;
  1. MV からデータを選択します。次のコードを参照してください。
    strata=> select t1_id, t1_lookup_id, t1_code, t2_id, t2_desc, t3_lookup_desc from mv_fast_refresh_funct_test order by t1_id;
     t1_id | t1_lookup_id | t1_code | t2_id | t2_desc  | t3_lookup_desc
    -------+--------------+---------+-------+----------+----------------
         1 |           10 | hello   |     1 | house    | ENGLAND
         2 |           20 | bye     |     2 | flat     | WALES
         3 |           30 | cya     |     3 | bungalow | SCOTLAND
         4 |           50 | goodbye |     4 | palace   | FRANCE
         5 |           50 | hi      |     5 | office   | FRANCE
    (5 rows) 

テストハーネスの更新

これは、高速リフレッシュプロセスがどのように機能するかを示す非常に単純なテストです。この記事では、ベーステーブル t1 の行を更新し、高速リフレッシュ後にそれが MV にどのように反映されるかを確認します。このテストスクリプトは update_mv_test.sh です。

  1. 次のように、テーブル t1 の行をコード UPDATE testpocsource.t1 set code='yo' where code='hello' で更新します。
    INFO: Changing a row for table t1
    UPDATE testpocsource.t1 set code='yo' where code='hello'
    Press the enter key to do the update...
    UPDATE 1
  2. MV をチェックしデータが変更されていないことを確認します (データがリフレッシュされていないため)。次のコードは、まだ設定が hello になっていることを示しています。
    INFO: Check the output from the MV
    Select * from mv_fast_refresh_funct_test order by t1_id;
    Press the enter key to see the MV output...
     t1_id | t1_lookup_id | t1_code | t2_id | t2_desc  | t3_lookup_desc
    -------+--------------+---------+-------+----------+----------------
         1 |           10 | hello   |     1 | house    | ENGLAND
         2 |           20 | bye     |     2 | flat     | WALES
         3 |           30 | cya     |     3 | bungalow | SCOTLAND
         4 |           50 | goodbye |     4 | palace   | FRANCE
         5 |           50 | hi      |     5 | office   | FRANCE
    (5 rows)
  1. MV で高速リフレッシュを実行して、もう一度確認してください。次のコードは、行が変更されたことを示しています。
    INFO: Let’s do a MV fast refresh to sync the MV with the change
    Press the enter key to do the MV refresh...
    NOTICE:  Fast Snapshot Refresh took 00:00:00.055291
    
    
    CONTEXT:  PL/pgSQL function inline_code_block line 12 at RAISE
    DO
    INFO: Now Check the output from the MV the code for t1_id is now yo
    Press the enter key to see the MV output...
     t1_id | t1_lookup_id | t1_code | t2_id | t2_desc  | t3_lookup_desc
    -------+--------------+---------+-------+----------+----------------
         1 |           10 | yo      |     1 | house    | ENGLAND
         2 |           20 | bye     |     2 | flat     | WALES
         3 |           30 | cya     |     3 | bungalow | SCOTLAND
         4 |           50 | goodbye |     4 | palace   | FRANCE
         5 |           50 | hi      |     5 | office   | FRANCE
    (5 rows)
  1. t1 テーブルを更新し、ビューを再度リフレッシュして、行を hello に戻します。次のコードを参照してください。
    INFO: Now lets change the code back to hello
    UPDATE testpocsource.t1 set code='hello' where code='yo'
    Press the enter key to do the update...
    UPDATE 1
    INFO: Lets do a MV fast refresh to sync the MV with the change
    Press the enter key to do the MV refresh...
    NOTICE:  Fast Snapshot Refresh took 00:00:00.021894
    
    
    CONTEXT:  PL/pgSQL function inline_code_block line 12 at RAISE
    DO
    INFO: Now Check the output from the MV the code for t1_id its back to hello
    Press the enter key to see the MV output...
     t1_id | t1_lookup_id | t1_code | t2_id | t2_desc  | t3_lookup_desc
    -------+--------------+---------+-------+----------+----------------
         1 |           10 | hello   |     1 | house    | ENGLAND
         2 |           20 | bye     |     2 | flat     | WALES
         3 |           30 | cya     |     3 | bungalow | SCOTLAND
         4 |           50 | goodbye |     4 | palace   | FRANCE
         5 |           50 | hi      |     5 | office   | FRANCE
    (5 rows)

テストハーネスの取り外し

テストハーネスを削除するには、drop_test_harness.sh スクリプトを実行します。これにより、データベースからデータと MV スキーマが削除されます。次のコードを参照してください。

-bash-4.2$ ./drop_test_harness.sh
INFO: Drop Complete check logfile for status - /tmp/test_harness_drop_20191217-1300.log

高速リフレッシュ機能を削除する

データベースから高速リフレッシュ機能を削除するには、dropFastRefreshModule.sh スクリプトを実行します。これにより、Y を入力してオブジェクトの削除の続行を確認するように求められます。次のコードを参照してください。

-bash-4.2$ ./dropFastRefreshModule.sh
Are you sure you want to remove the module schema - testpoc (y/n)?y
yes selected the schemas - testpoc will be dropped

まとめ

この記事では、高速リフレッシュモジュールを確認し、MV を起動して実行する簡単な例を示しました。お客様がリアルタイムのビジネスインテリジェンスにアクセスできるようにするには、独自のソリューションを見つける必要がありました。PostgreSQL で MV ログを使用して高速リフレッシュ機能をどう開発するかをご紹介しましたが、楽しんでいただけましたでしょうか。コードは GitHub リポジトリからダウンロードできます。

このシリーズのパート 2 にご期待ください。パート 2 ではモジュールを詳述し、本番環境でこのソリューションを実行して得られた結果について検討します。

 


著者について

Tony Mullen は、CDL のプリンシパルデータエンジニアです。彼は主に Oracle でデータベースに 18 年以上携わってきた経験があり、近年では PostgreSQL に取り組んでいます。彼は主要なアプリケーションの Oracle オンプレミスから Amazon RDS PostgreSQL への移行を率いています。また PostgreSQL マンチェスターの交流会を共催し、マンチェスターに PostgreSQL コミュニティを構築することに強い関心を持っています。