使用规则或通知自动刷新物化视图
Posted
技术标签:
【中文标题】使用规则或通知自动刷新物化视图【英文标题】:Refresh a materialized view automatically using a rule or notify 【发布时间】:2014-07-17 09:39:51 【问题描述】:我有一个很少更改(大约一天两次)的 PostgreSQL 9.3 数据库的物化视图。但是当它发生时,我想及时更新它的数据。
到目前为止,这是我的想法:
有一个物化视图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_update
将设置为 TRUE
。
table2
和第二行也是如此。
显然,如果need_update
为TRUE,则必须刷新物化视图。
更新:
由于物化视图不支持规则(正如@pozs 在下面的评论中提到的那样),我会更进一步。我会用定义“SELECT * FROM mat_view
”创建一个虚拟视图v_mat_view
。当用户在此视图上执行 SELECT 时,我需要在 SELECT 上创建一个规则,它执行以下操作:
mat_view
是否应该更新(SELECT 1 FROM config WHERE mat_view_name='mat_view' AND need_update=TRUE
)
用UPDATE config SET need_update=FALSE where mat_view_name='mat_view'
重置need_update
标志
REFRESH MATERIALIZED VIEW mat_view
最后以mat_view
为目标执行原始SELECT 语句。
更新2: 我尝试创建上述步骤:
创建一个处理上述四点的函数:
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;
创建真正从函数mat_view_selector
中选择的视图v_mat_view
:
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
所以基本上它确实有效,但性能可能是个问题。
有人有更好的主意吗? 如果没有,那么我将不得不在应用程序逻辑中以某种方式实现它,或者更糟:运行一个每分钟左右运行的简单 cronjob。
【问题讨论】:
有趣的方法。您是否尝试过替换视图的"_RETURN"
规则?应该很简单postgresql.org/docs/current/static/sql-createrule.html
你能改写你的问题并详细说明一下吗?即使阅读了您发布的链接,我也不明白您的意思。
视图默认有一个"_RETURN"
规则。您想在返回视图的原始选择之前替换它以刷新您的物化视图。你试过了吗?
没关系,在物化视图上重新定义规则(存在!)会出现错误rules on materialized views are not supported SQL state: 0A000
【参考方案1】:
您应该在 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();
通过这种方式,您的物化视图始终是最新的。这种简单的解决方案可能难以接受频繁的插入/更新和零星的选择。 就您而言(很少一天更换两次),它非常适合您的需求。
要实现物化视图的延迟刷新,您需要以下功能之一:
异步触发 选择前触发 选择之前的规则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;
在实践中是否可以接受取决于我不知道的细节。
【讨论】:
这只对我来说是可行的,如果刷新可以异步进行,那么使用 table1 和 table2 的应用程序用户不需要等待不必要的长时间。使用 mat_view 的用户可以接受稍等片刻。 您可以通过将@klin 定义的触发器替换为通知命令(而不是 refresh_mat_view())来模拟异步触发器。您将需要一个外部进程来监听该通知并运行 refresh_mat_view()。那是在 postgres 之外。它确实摆脱了事务绑定/等待 table1/table2 更新程序。我喜欢本文底部的命令:gonzalo123.com/2012/11/26/… about using a table to queue async triggers. @Greg - 是的,这几乎就是我在上面的问题中所写的“配置”表所做的事情。 这不应该是一个SQL特性吗?喜欢CREATE AUTO REFRESH MATERIALIZED VIEW
?
这个答案在 2021 年仍然成立吗?刷新物化视图不会重新计算整个表并阻止所有访问,直到刷新完成?【参考方案2】:
PostgreSQL 9.4 将 REFRESH CONCURRENTLY
添加到物化视图中。
当您描述尝试设置物化视图的异步更新时,这可能是您正在寻找的内容。
在刷新完成之前,从物化视图中选择的用户将看到不正确的数据,但在许多使用物化视图的场景中,这是一个可以接受的折衷方案。
使用语句级触发器来监视基础表的任何更改,然后同时刷新实体化视图。
【讨论】:
REFRESH CONCURRENTLY
不是 OP 正在寻找的东西。使用此选项刷新仍然会阻止执行,直到它完成。事实上,在较大的数据集上,它可能比简单的刷新要慢,并且还需要对视图进行唯一的约束。 See the documentation
? REFRESH CONCURRENTLY
不会阻止读取的执行。并发SELECT
s 只需获取旧版本的视图,直到刷新完成,然后换入新副本。
查看 OP 对我的回答的第一条评论。他不希望更新表的用户在触发器中等待刷新完成。 REFRESH CONCURRENTLY
在这里不为用户做任何事情(它不会阻止其他会话)。
如何在 REFRESH CONCURRENTLY 完成时收到通知?我想在物化视图刷新后对其执行连接
@PirateApp 你等到REFRESH CONCURRENTLY
完成执行。它允许在运行时执行查询,但在刷新完成之前它仍然不会将控制权返回给您的程序。如果你想通知其他线程,你必须找到另一个方法来做到这一点。以上是关于使用规则或通知自动刷新物化视图的主要内容,如果未能解决你的问题,请参考以下文章