web-dev-qa-db-ja.com

ルールまたは通知を使用してマテリアライズドビューを自動的に更新する

PostgreSQL 9.3データベースのマテリアライズドビューがあり、めったに変更されません(1日に2回程度)。しかし、そうなったら、すぐにデータを更新したいと思います。

ここに私がこれまで考えていたことがあります:

マテリアライズドビューmat_viewがあります。これは、結合ステートメントを使用して、テーブルtable1およびtable2からデータを取得します。

table1またはtable2の何かが変更されるたびに、すでに小さな構成テーブルconfigを更新するトリガーがあります。

table_name | mat_view_name | need_update
-----------+---------------+------------
table1     | mat_view      | TRUE/FALSE
table2     | mat_view      | TRUE/FALSE

したがって、table1のいずれかが変更された場合(すべてのステートメントのUPDATEおよびDELETEのトリガーがある場合)、最初の行のフィールドneed_updateTRUEに設定されます。 table2と2行目も同様です。

明らかに、need_updateがTRUEの場合、マテリアライズドビューを更新する必要があります。

[〜#〜] update [〜#〜]:マテリアライズドビューはルールをサポートしていないため(@pozsが以下のコメントで言及されているように)、さらに一歩進めます。定義 "v_mat_view"でダミービューSELECT * FROM mat_viewを作成します。ユーザーがこのビューでSELECTを実行すると、次のことを行うルールON SELECTを作成する必要があります。

  • mat_viewを更新する必要があるかどうかを確認します(SELECT 1 FROM config WHERE mat_view_name='mat_view' AND need_update=TRUE
  • need_updateフラグをUPDATE config SET need_update=FALSE where mat_view_name='mat_view'でリセットします
  • REFRESH MATERIALIZED VIEW mat_view
  • 最後に、元のSELECTステートメントを実行しますが、mat_viewをターゲットとして使用します。

PDATE2:上記の手順を作成してみました:

上記の4つのポイントを処理する関数を作成します。

CREATE OR REPLACE FUNCTION mat_view_selector()
RETURNS SETOF mat_view AS $body$
BEGIN
  -- here is checking whether to refresh the mat_view
  -- then return the select:
  RETURN QUERY SELECT * FROM mat_view;
END;
$body$ LANGUAGE plpgsql;

関数v_mat_viewから実際に選択するビューmat_view_selectorを作成します。

CREATE TABLE v_mat_view AS SELECT * from mat_view LIMIT 1;
DELETE FROM v_mat_view;

CREATE RULE "_RETURN" AS
    ON SELECT TO v_mat_view
    DO INSTEAD 
        SELECT * FROM mat_view_selector();
    -- this also converts the empty table 'v_mat_view' into a view.

結果は満足のいくものではありません。

# explain analyze select field1 from v_mat_view where field2 = 44;
QUERY PLAN
Function Scan on mat_view_selector (cost=0.25..12.75 rows=5 width=4)
(actual time=15.457..18.048 rows=1 loops=1)
Filter: (field2 = 44)
Rows Removed by Filter: 20021
Total runtime: 31.753 ms

mat_view自体からの選択と比較して:

# explain analyze select field1 from mat_view where field2 = 44;
QUERY PLAN
Index Scan using mat_view_field2 on mat_view (cost=0.29..8.30 rows=1 width=4)
  (actual time=0.015..0.016 rows=1 loops=1)
Index Cond: (field2 = 44)
Total runtime: 0.036 ms

本質的には機能しますが、パフォーマンスが問題になる場合があります。

誰もがより良いアイデアを持っていますか?そうでない場合は、アプリケーションロジックでそれを何らかの方法で実装する必要があります。さらに悪いことには、1分ごとに実行される単純なcronjobを実行します。 :-(

59
mawimawi

PostgreSQL 9.4追加REFRESH CONCURRENTLYからマテリアライズドビューへ。

これは、マテリアライズドビューの非同期更新をセットアップしようとするときに説明するものです。

マテリアライズドビューから選択するユーザーには、更新が完了するまで誤ったデータが表示されますが、マテリアライズドビューを使用する多くのシナリオでは、これは許容できるトレードオフです。

基礎となるテーブルの変更を監視し、マテリアライズドビューを同時に更新するステートメントレベルトリガーを使用します。

25
Jeff Widman

table1およびtable2の各ステートメントの挿入/更新/削除/切り捨て後に、トリガーのビューを更新する必要があります。

create or replace function refresh_mat_view()
returns trigger language plpgsql
as $$
begin
    refresh materialized view mat_view;
    return null;
end $$;

create trigger refresh_mat_view
after insert or update or delete or truncate
on table1 for each statement 
execute procedure refresh_mat_view();

create trigger refresh_mat_view
after insert or update or delete or truncate
on table2 for each statement 
execute procedure refresh_mat_view();

このようにして、マテリアライズドビューは常に最新の状態になります。この単純なソリューションは、頻繁な挿入/更新および散発的な選択では受け入れがたい場合があります。あなたの場合(1日2回ほど変更されることはほとんどありません)、理想的にはあなたのニーズに合っています。


マテリアライズドビューのdeferred refreshを実現するには、次の機能のいずれかが必要です。

  • 非同期トリガー
  • 選択前にトリガー
  • 前選択のルール

Postgresにはそれらがないため、clear postgresソリューションはないようです。

これを考慮に入れて、mat_viewの選択用のラッパー関数を検討します。

CREATE OR REPLACE FUNCTION select_from_mat_view(where_clause text)
RETURNS SETOF mat_view AS $body$
BEGIN
  -- here is checking whether to refresh the mat_view
  -- then return the select:
  RETURN QUERY EXECUTE FORMAT ('SELECT * FROM mat_view %s', where_clause);
END;
$body$ LANGUAGE plpgsql;

実際に受け入れられるかどうかは、私が知らない詳細に依存します。

103
klin