PostgreSQLでマテリアライズドビューを段階的に更新する


33

PostgreSQLでマテリアライズドビューを段階的に更新することはできますか。つまり、新規または変更されたデータに対してのみですか。

次の表とマテリアライズドビューを検討してください。

CREATE TABLE graph (
   xaxis integer NOT NULL,
   value integer NOT NULL,
);

CREATE MATERIALIZED VIEW graph_avg AS 
SELECT xaxis, AVG(value)
FROM graph
GROUP BY xaxis

定期的に、新しい値が追加されるgraphか、既存の値が更新されます。graph_avg更新された値についてのみ、数時間ごとにビューを更新します。ただし、PostgreSQL 9.3では、テーブル全体が更新されます。これにはかなり時間がかかります。次のバージョン9.4ではCONCURRENT更新が可能ですが、ビュー全体が更新されます。何億行もあるため、これには数分かかります。

更新された新しい値を追跡し、部分的にのみビューを更新する良い方法は何ですか?

回答:


22

「マテリアライズドビュー」として機能する独自のテーブルをいつでも実装できます。それはあなたが前にしなければならなかったことですMATERIALIZED VIEWは、Postgres 9.3でいずれに実装されるに。

たとえば、プレーンを作成できますVIEW

CREATE VIEW graph_avg_view AS 
SELECT xaxis, AVG(value) AS avg_val
FROM   graph
GROUP  BY xaxis;

そして、結果全体を一度、または最初からやり直す必要があるたびに具体化します。

CREATE TABLE graph_avg AS
SELECT * FROM graph_avg_view

(またはSELECTVIEW。を作成せずにステートメントを直接使用します。)
次に、ユースケースの非公開の詳細に応じて、DELETE/ UPDATE/INSERT手動で変更します。

テーブルのデータ変更CTEをそのまま使用した基本的なDMLステートメント

誰も同時に書き込みをしようとしないと仮定しgraph_avgます(読み取りは問題ありません):

WITH del AS (
   DELETE FROM graph_avg t
   WHERE  NOT EXISTS (SELECT 1 FROM graph_avg_view v WHERE v.xaxis = v.xaxis);
   )
, upd AS (
   UPDATE graph_avg t
   FROM   graph_avg_view v
   WHERE  t.xaxis = v.xaxis
   AND    t.avg_val <> v.avg_val
   )
INSERT INTO graph_avg t
SELECT *
FROM   graph_avg_view v
LEFT   JOIN graph_avg t USING (xaxis)
WHERE  t.xaxis IS NULL;

しかし、これはおそらく最適化されるべきです。

基本的なレシピ:

  • 基本表にtimestampデフォルトの列を追加しnow()ます。それを呼び出しましょうts
    • 更新がある場合は、トリガーを追加して、xaxisまたはを変更するすべての更新で現在のタイムスタンプを設定しますvalue
  • 最新のスナップショットのタイムスタンプを記憶する小さなテーブルを作成します。それを呼び出しましょうmv

    CREATE TABLE mv (
       tbl text PRIMARY KEY
     , ts timestamp NOT NULL DEFAULT '-infinity'
    ); -- possibly more details
  • この部分的な複数列インデックスを作成します。

    CREATE INDEX graph_mv_latest ON graph (xaxis, value)
    WHERE  ts >= '-infinity';
  • クエリの述語として最後のスナップショットのタイムスタンプを使用して、完全なインデックス使用でスナップショットを更新します。

  • トランザクションの最後に、インデックスを削除し、インデックス述語のタイムスタンプを(最初に'-infinity')トランザクションタイムスタンプで置き換えて再作成 します。これはテーブルにも保存します。1つのトランザクションですべて。

  • なお、部分インデックスがカバーに偉大であることINSERT及びUPDATE操作ではなくDELETE。それをカバーするには、テーブル全体を考慮する必要があります。すべて厳密な要件に依存します。


実体化されたビューを明確にし、別の答えを提案してくれてありがとう。
user4150760 14

13

同時更新(Postgres 9.4)

要望通りの増分更新ではありませんが、Postgres 9.4は新しい同時更新機能を提供します。

ドキュメントを引用するには…

PostgreSQL 9.4より前では、マテリアライズドビューの更新はテーブル全体をロックすることを意味し、そのためクエリを実行することはできませんでした。後続のクエリを保留しています。これは、CONCURRENTLYキーワードを使用して軽減できます。

 postgres=# REFRESH MATERIALIZED VIEW CONCURRENTLY mv_data;

ただし、マテリアライズドビューに一意のインデックスが存在する必要があります。マテリアライズドビューをロックする代わりに、一時的に更新されたバージョンを作成し、2つのバージョンを比較してから、マテリアライズドビューに対してINSERTとDELETEを適用して差を適用します。つまり、クエリは、更新中もマテリアライズドビューを使用できます。非コンカレント形式とは異なり、タプルは固定されておらず、前述のDELETEにより無効なタプルが残るため、VACUUMが必要です。

この同時更新は、完全な新しいクエリを実行しています(増分ではありません)。したがって、CONCURRENTLYは計算時間全体を節約せず、更新中にマテリアライズドビューを使用できない時間を最小限に抑えます。


11
ちょっと読んでみるまで興奮していました。it instead creates a temporary updated version of it...compares the two versions-これは、一時的に更新されたバージョンがまだ完全な計算であることを意味し、既存のビューに差異を適用します。本質的に、私はまだすべての計算をやり直していますが、一時テーブルで行っています。
user4150760 14

5
ああ、本当です。CONCURRENTLY全体の計算時間を節約するのではなく、更新中にマテリアライズドビューを使用できない時間を最小限に抑えます。
バジルブルク14
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.