考虑此表和实例化视图:
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
更新,但仍刷新整个视图。对于亿万行,这需要几分钟。 什么是跟踪更新和新值并仅部分刷新视图的好方法?
#1 楼
您始终可以实现自己的表,作为“实例化视图”。这就是我们在Postgres 9.3中实现MATERIALIZED VIEW
之前所做的工作。 您可以创建一个简单的
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 FROM graph_avg_view WHERE xaxis = t.xaxis)
)
, upd AS (
UPDATE graph_avg t
SET avg_val = v.avg_val
FROM graph_avg_view v
WHERE t.xaxis = v.xaxis
AND t.avg_val <> v.avg_val
-- AND t.avg_val IS DISTINCT FROM v.avg_val -- alt if avg_val can be NULL
)
INSERT INTO graph_avg t -- no target list, whole row
SELECT v.*
FROM graph_avg_view v
WHERE NOT EXISTS (SELECT FROM graph_avg WHERE xaxis = v.xaxis);
基本食谱
将带有默认
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'
),您也将其保存到表中。一次交易中的所有内容。请注意,部分索引可以很好地覆盖
INSERT
和UPDATE
操作,但不能覆盖DELETE
。为此,您需要考虑整个表格。这完全取决于确切的要求。#2 楼
并发更新(Postgres 9.4)虽然您没有要求增量更新,但Postgres 9.4确实提供了新的并发更新功能。
引用文档…
在PostgreSQL 9.4之前,刷新物化视图意味着锁定整个表,因此防止任何查询它,如果刷新花费很长时间来获取排他锁(它等待查询)使用它来完成),反过来又阻止了后续查询。现在,可以使用CONCURRENTLY关键字来缓解这种情况:
postgres=# REFRESH MATERIALIZED VIEW CONCURRENTLY mv_data;
但是,物化视图上将需要存在唯一索引。它没有锁定实例化视图,而是创建了它的临时更新版本,比较这两个版本,然后对实例化视图应用INSERT和DELETE以应用差异。这意味着查询在更新时仍然可以使用实例化视图。
与非并发形式不同,元组不会被冻结,由于前面提到的DELETE会留下无效的元组,因此它需要VACUUMing。 >
此并发更新仍在执行完整的全新查询(不是增量查询)。因此,CONCURRENTLY不会节省总的计算时间,而只是将您的物化视图在更新期间不可用的时间减至最少。
评论
有一阵子我很兴奋,直到我仔细阅读为止。相反,它创建了它的临时更新版本...比较两个版本-这意味着临时更新版本仍是完整计算,然后将差异应用于现有视图。因此从本质上讲,我仍在重新执行所有计算,只是在临时表中进行。
–user4150760
2014-12-30 7:02
嗯,是的,CONCURRENTLY不会节省总的计算时间,只是将您的物化视图在更新期间不可用的时间减至最少。
–罗勒·布尔克
2014年12月30日15:50
从postgres 11或12开始,这仍然正确吗?
– PirateApp
20-2-24在16:47
评论
感谢您对实例化视图的清晰说明,并提出替代答案。
–user4150760
2014年12月23日下午5:28