Автоматическое обновление материализованного представления с помощью правила или уведомления

у меня есть материализованное представление о базе данных PostgreSQL 9.3, которая редко меняется (около двух раз в день). Но когда это произойдет, я хотел бы быстро обновить свои данные.

вот о чем я думал до сих пор:

есть материализованный вид mat_view который получает свои данные из таблиц table1 и table2 используя некоторую инструкцию join.

всякий раз, когда что-то в 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 упоминается в комментарии ниже), я сделал бы еще один шаг. Я бы создал фиктивный вид v_mat_view с определением "SELECT * FROM mat_view". Когда пользователь делает выбор в этом представлении, мне нужно создать правило выбора, которое делает следующее:

  • проверить, является ли 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 как цель.

обновление 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;

создать вид 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.

результат неудовлетворительный:

# 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, который работает каждую минуту или около того. :-(

2 ответов


добавлен PostgreSQL 9.4 REFRESH CONCURRENTLY к материализованным представлениям.

это может быть то, что вы ищете, когда описываете попытку установки асинхронного обновления материализованного представления.

пользователи, выбирающие из материализованного представления, будут видеть неправильные данные до завершения обновления, но во многих сценариях, использующих материализованное представление, это приемлемый компромисс.

используйте триггер уровня оператора, который следит за базовыми таблицами для любых изменений и затем одновременно обновляет материализованное представление.


вы должны обновить представление в триггерах после вставки / обновления / удаления/усечения для каждого оператора на 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 не имеет ни одного из них, поэтому кажется, что нет ясный решение postgres.

учитывая это, я бы рассмотрел функцию-оболочку для selects на 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;

если это приемлемо на практике зависит от подробностей я не знаю о.