使用规则或通知自动刷新实例化视图

maw*_*awi 60 postgresql triggers materialized-views postgresql-9.3

我在PostgreSQL 9.3数据库上有一个物化视图,该数据库很少发生变化(大约每天两次).但是当它发生时,我想及时更新其数据.

以下是我到目前为止的想法:

有一个物化视图mat_view,它从表中获取数据table1table2使用一些连接语句.

无论何时进入table1table2更改,我已经有一个触发器更新一个config由...组成的配置表

table_name | mat_view_name | need_update
-----------+---------------+------------
table1     | mat_view      | TRUE/FALSE
table2     | mat_view      | TRUE/FALSE
Run Code Online (Sandbox Code Playgroud)

因此,如果有任何table1更改(每个语句的UPDATE和DELETE都有触发器),need_update则第一行中的字段设置为TRUE.同样适用table2于第二行.

显然,如果need_update为TRUE,则必须刷新实体化视图.

更新:由于物化视图不支持规则(如下面评论中提到的@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作为目标.

UPDATE2:我尝试创建上面的步骤:

创建一个处理上述四点的函数:

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;
Run Code Online (Sandbox Code Playgroud)

创建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.
Run Code Online (Sandbox Code Playgroud)

结果令人不满意:

# 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
Run Code Online (Sandbox Code Playgroud)

与从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
Run Code Online (Sandbox Code Playgroud)

所以基本上它可以工作,但性能可能是一个问题.

谁有更好的想法?如果没有,那么我将不得不以某种方式在应用程序逻辑中实现它,或者更糟糕的是:运行一个每分钟运行一次的简单cronjob.:-(

kli*_*lin 103

你应该插入/更新后刷新触发器浏览/删除/截断对每个语句table1table2.

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();
Run Code Online (Sandbox Code Playgroud)

通过这种方式,您的物化视图始终是最新的.对于频繁的插入/更新和零星选择,这个简单的解决方案可能很难接受.在您的情况下(很少每天更换两次),它非常适合您的需求.


要实现物化视图的延迟刷新,您需要以下功能之一:

  • 异步触发器
  • 在选择之前触发
  • 以前选择的规则

Postgres没有,所以似乎没有明确的 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;
Run Code Online (Sandbox Code Playgroud)

如果在实践中可接受取决于我不知道的细节.

  • 这不应该是 SQL 的功能吗?喜欢“创建自动刷新物化视图”? (4认同)
  • 您可以通过使用notify命令(而不是refresh_mat_view())替换@klin定义的触发器来模拟异步触发器.您将需要一个侦听该通知的外部进程并运行refresh_mat_view().这是在postgres之后重新进入.它确实摆脱了事务绑定/等待table1/table2更新程序.我喜欢本文底部的命令:http://gonzalo123.com/2012/11/26/sending-sockets-from-postgresql-triggers-with-python/关于使用表来排队异步触发器. (2认同)

Jef*_*man 25

PostgreSQL 9.4添加REFRESH CONCURRENTLY到物化视图.

当您描述尝试设置物化视图的异步更新时,这可能是您正在寻找的.

从物化视图中选择的用户将在刷新完成之前看到不正确的数据,但在使用物化视图的许多场景中,这是可接受的权衡.

使用语句级触发器监视基础表以查找任何更改,然后同时刷新实例化视图.

  • “同时刷新”**不是OP正在寻找的**。使用此选项刷新仍会阻止执行,直到完成。事实上,在较大的数据集上,它可能比简单的刷新慢,并且还需要对视图进行唯一的约束。[参见文档](https://www.postgresql.org/docs/9.6/static/sql-refreshmaterializedview.html) (3认同)
  • ?`REFRESH CONCURRENTLY`将不会阻止读取的执行。并发SELECT只是获取视图的旧版本,直到刷新完成,然后才换入新副本。 (2认同)