Поступово оновлювати матералізований вигляд у 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оновлювати, але вона все ще оновлює весь погляд. Зі 100 мільйонами рядків це займає кілька хвилин.

Який хороший спосіб відстежувати оновлені та нові значення та лише частково оновлювати перегляд?

Відповіді:


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 з КТРОМ даних змін для вашої таблиці , як є :

Припускаючи , що ніхто інший НЕ намагається писати в 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'), яку ви також збережете у своїй таблиці. Все за одну транзакцію.

  • Зауважте, що частковий індекс чудово підходить для покриття INSERTта UPDATEоперацій, але ні DELETE. Щоб покрити це, вам потрібно розглянути всю таблицю. Все залежить від точних вимог.


Дякую за ясність у матеріалізованих поглядах та пропонуючи альтернативну відповідь.
user4150760

13

Одночасне оновлення (Postgres 9.4)

Хоча це не додаткове оновлення, як ви просили, Postgres 9.4 надає нову функцію одночасного оновлення .

Процитувати док ...

До PostgreSQL 9.4 оновлення матеріалізованого представлення означало блокування всієї таблиці, а отже, запобігання будь-якому запиту до неї, і якщо оновлення потребувало тривалого часу, щоб придбати ексклюзивний замок (поки він чекає, коли запити, використовуючи його, закінчать), він у свою чергу зберігає наступні запити. Тепер це можна пом'якшити за допомогою СУЧАСНОГО ключового слова:

 postgres=# REFRESH MATERIALIZED VIEW CONCURRENTLY mv_data;

Однак у матеріалізованому поданні повинен існувати унікальний індекс. Замість блокування матеріалізованого перегляду, він замість цього створює тимчасову оновлену версію, порівнює дві версії, а потім застосовує INSERTs та DELETE проти матеріалізованого представлення для застосування різниці. Це означає, що запити все ще можуть використовувати матеріалізований вигляд під час оновлення. На відміну від форми, що не відповідає одночасно, кортежі не заморожуються, і він потребує ВАКУУМУВАННІ через вищезгадані ВИДАЛЕННЯ, які залишать мертві кортежі позаду.

Це одночасне оновлення все ще виконує повний свіжий запит (не поступовий). Таким чином, КОНЦУРЕНТНО не економить на загальному часу на обчислення, він просто мінімізує кількість часу, коли ваш матеріалізований вигляд недоступний для використання під час його оновлення.


11
На якусь мить я був схвильований, поки не прочитав уважно. it instead creates a temporary updated version of it...compares the two versions- Це означає, що тимчасово оновлена ​​версія все ще є повним обчисленням, тоді вона застосовує різницю до існуючого перегляду. Отже, по суті, я все ще роблю ВСІ обчислення, але просто у тимчасовій таблиці.
user4150760

5
Ах, правда, CONCURRENTLYне економиться на загальному часу обчислення, він просто мінімізує кількість часу, коли ваш матеріалізований вигляд недоступний для використання під час його оновлення.
Василь Бурк
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.