在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更新,但仍刷新整个视图。对于亿万行,这需要几分钟。

跟踪更新和新值并仅部分刷新视图的好方法是什么?

Answers:


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

(或者使用SELECT直接的语句,而无需创建一个VIEW。)
然后,根据你的使用情况未披露的细节,你可以DELETE/ UPDATE/ INSERT手动更改。

一个基本的DML语句,其中包含用于表的数据修改CTE,如下所示

假设没有其他人试图写入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')来重新创建索引,该谓词 也要保存到表中。一切都在一次交易中。

  • 请注意,部分索引可以很好地覆盖INSERTUPDATE操作,但不是DELETE。为此,您需要考虑整个表格。这完全取决于确切的要求。


感谢您对实例化视图的清晰说明,并提出替代答案。
user4150760

13

并发更新(Postgres 9.4)

虽然不是您要求的增量更新,但Postgres 9.4确实提供了新的并发更新功能。

引用文档…

在PostgreSQL 9.4之前,刷新实例化视图意味着锁定整个表,从而阻止任何查询它,并且如果刷新花费很长时间来获取排他锁(它等待使用它的查询完成),则反过来正在阻止后续查询。现在可以使用CONCURRENTLY关键字来缓解此问题:

 postgres=# REFRESH MATERIALIZED VIEW CONCURRENTLY mv_data;

但是,物化视图上将需要存在唯一索引。它没有锁定实例化视图,而是创建了它的临时更新版本,比较这两个版本,然后对实例化视图应用INSERT和DELETE以应用差异。这意味着查询在更新时仍可以使用实例化视图。与非并发形式不同,元组不会被冻结,并且由于上述DELETE会留下死元组,因此需要对其进行真空处理。

此并发更新仍在执行完整的全新查询(不是增量查询)。因此,CONCURRENTLY不会节省总的计算时间,而只是将您的物化视图在更新期间不可用的时间减至最少。


11
有一阵子我很兴奋,直到我仔细阅读为止。it instead creates a temporary updated version of it...compares the two versions-这意味着临时更新的版本仍然是完整的计算,然后将差异应用于现有视图。因此,从本质上讲,我仍在重新执行所有计算,只是在临时表中进行。
user4150760 2014年

5
嗯,是的,CONCURRENTLY并不能节省整体计算时间,它只是将您的物化视图在更新期间不可用的时间减至最少。
罗勒·布尔克
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.