Как я могу гарантировать, что материализованное представление всегда актуально?
Мне нужно будет вызывать REFRESH MATERIALIZED VIEW при каждом изменении таблиц, не так ли? Я удивлен, что не нашел много обсуждений этого в интернете.
Как я должен это сделать?
Я думаю, что верхняя половина ответа - это то, что я ищу: https://stackoverflow.com/a/23963969/168143
Есть ли в этом какая-то опасность? Если обновление представления завершится неудачей, будет ли транзакция по вызову update, insert и т. д. откатиться назад? (вот чего я хочу... Я подумайте)
2 ответов:
Да, PostgreSQL сам по себе никогда не вызовет его автоматически, вам нужно сделать это каким-то образом.Мне нужно будет вызывать
REFRESH MATERIALIZED VIEWпри каждом изменении соответствующих таблиц, верно?Как я должен это делать?
Много способов достичь этого. Прежде чем привести несколько примеров, имейте в виду, что
REFRESH MATERIALIZED VIEWкоманда блокирует представление в режиме AccessExclusive, поэтому, пока она работает, вы даже не можете сделатьSELECTна стол.Хотя, если вы находитесь в версии 9.4 или новее, вы можете дать ему опцию
CONCURRENTLY:REFRESH MATERIALIZED VIEW CONCURRENTLY my_mv;Это приобретет исключительную блокировку и не будет блокировать запросы
SELECT, но может иметь большие накладные расходы (зависит от количества измененных данных, если несколько строк изменились, то это может быть быстрее). Хотя вы все еще не можете запустить две командыREFRESHодновременно.Обновить вручную
Это вариант, который следует рассмотреть. Особенно в случаях загрузки данных или пакетной обработки обновления (например, система, которая загружает только тонны информации / данных после длительных периодов времени) обычно есть операции в конце для изменения или обработки данных, поэтому вы можете просто включить операцию
REFRESHВ конце ее.Планирование операции обновления
Первый и широко используемый вариант-использовать некоторую систему планирования для вызова обновления, например, вы можете настроить подобное в задании cron:
*/30 * * * * psql -d your_database -c "REFRESH MATERIALIZED VIEW CONCURRENTLY my_mv"И тогда ваш материализованный взгляд будет обновляется каждые 30 минут.
Соображения
Эта опция действительно хороша, особенно с опцией
CONCURRENTLY, но только если вы можете принять данные, которые не всегда на 100% актуальны. Имейте в виду, что даже сCONCURRENTLYили безREFRESHкомандаREFRESHдолжна выполнить весь запрос, поэтому вам нужно потратить время, необходимое для выполнения внутреннего запроса, прежде чем рассматривать время для планированияREFRESH.Обновление с помощью триггера
Другой вариант-позвонить
REFRESH MATERIALIZED VIEWв триггерной функции, например:CREATE OR REPLACE FUNCTION tg_refresh_my_mv() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN REFRESH MATERIALIZED VIEW CONCURRENTLY my_mv; RETURN NULL; END; $$;Затем в любой таблице, которая включает изменения в представлении, вы делаете:
CREATE TRIGGER tg_refresh_my_mv AFTER INSERT OR UPDATE OR DELETE ON table_name FOR EACH STATEMENT EXECUTE PROCEDURE tg_refresh_my_mv();Соображения
Он имеет некоторые критические подводные камни для производительности и параллелизма:
Единственная ситуация, которую я могу считать хорошей идеей, - это если изменения действительно редки.
- любая операция вставки / обновления / удаления должна будет выполнить запрос (что возможно медленно, если вы рассматриваете MV);
- даже с
CONCURRENTLY, одинREFRESHпо-прежнему блокирует другой, поэтому любая вставка / обновление / удаление в задействованных таблицах будет быть сериализованным.Обновить с помощью LISTEN/NOTIFY
Проблема с предыдущим вариантом заключается в том, что он является синхронным и накладывает большие накладные расходы на каждую операцию. Чтобы исправить это, вы можете использовать триггер, как и раньше, но это вызывает толькоNOTIFYоперация :Таким образом, вы можете построить приложение, которое поддерживает связь и используетCREATE OR REPLACE FUNCTION tg_refresh_my_mv() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN NOTIFY refresh_mv, 'my_mv'; RETURN NULL; END; $$;LISTENоперация для определения необходимости вызоваREFRESH. Один хороший проект, который вы можете использовать для тестирования, это pgsidekick , с этим проектом вы можете использовать shell script для выполненияLISTEN, поэтому вы можете запланироватьREFRESHкак:pglisten --listen=refresh_mv --print0 | xargs -0 -n1 -I? psql -d your_database -c "REFRESH MATERIALIZED VIEW CONCURRENTLY ?;"Или используйте
pglater(также внутриpgsidekick), чтобы убедиться, что вы не звонитеREFRESHочень часто. Например, вы можете использовать следующий триггер, чтобы сделать этоREFRESH, но в течение 1 минуты (60 секунд):CREATE OR REPLACE FUNCTION tg_refresh_my_mv() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN NOTIFY refresh_mv, '60 REFRESH MATERIALIZED VIEW CONCURRENLTY my_mv'; RETURN NULL; END; $$;Так что он не будет звонить
REFRESHменее чем через 60 секунд., а также если ВыNOTIFYмного раз менее чем за 60 секунд, тоREFRESHбудет срабатывать только один раз.Соображения
Как вариант cron, этот вариант также хорош, только если вы можете обнажить немного устаревших данных, но это имеет то преимущество, что
REFRESHвызывается только тогда, когда это действительно необходимо, поэтому у вас меньше накладных расходов, а также данные обновляются ближе к тому, когда это необходимо.OBS: я еще не пробовал коды и примеры, так что если кто-то найдет ошибку, опечатку или попробуйте и работает (или нет), пожалуйста, дайте мне знать.
Позвольте мне указать на три вещи на предыдущий ответ MatheusOl - технология pglater.
В качестве последнего элемента массива long_options он должен включать "{0, 0, 0, 0}" элемент, как указано на https://linux.die.net/man/3/getopt_long фразой "последний элемент массива должен быть заполнен нулями."Итак, оно должно гласить -
static struct option long_options[] = { //...... {"help", no_argument, NULL, '?'}, {0, 0, 0, 0} };На malloc / free thing -- one free (for char listen = Мэллок(...);) отсутствовать. Так или иначе, Мэллок вызвал сбой процесса pglater на CentOS (но не на Ubuntu - я не знаю, почему). Поэтому я рекомендую использовать массив char и назначить имя массива указателю char (как char, так и char**). Многие из вас должны принудительно преобразовать тип, пока вы делаете это(назначение указателя).
char block4[100]; ... password_prompt = block4; ... char block1[500]; const char **keywords = (const char **)&block1; ... char block3[300]; char *listen = block3; sprintf(listen, "listen %s", id); PQfreemem(id); res = PQexec(db, listen);Используйте нижеприведенную таблицу для вычисления времени ожидания, где md-mature_duration, которое является разницей во времени между последней точкой обновления (lr) и текущее время.
When md >= callback_delay (cd) = = > timeout: 0
, Когда МД + PING_INTERVAL >= кд ==> время ожидания: кд-МД[=кд-(сейчас-ЛР)]
Когда md + PING_INTERVAL timeout: PI
Чтобы реализовать этот алгоритм (3-й пункт), вы должны init 'lr' следующим образом -
res = PQexec(db, command); latest_refresh = time(0); if (PQresultStatus(res) == PGRES_COMMAND_OK) {
Comments