Периодически добавляются новые значения graphили обновляется существующее значение. Я хочу обновлять представление graph_avgкаждые пару часов только для значений, которые были обновлены. Однако в PostgreSQL 9.3 вся таблица обновляется. Это довольно много времени. Следующая версия 9.4 позволяет CONCURRENTобновлять, но обновляет весь вид. С сотнями миллионов строк это занимает несколько минут.
Какой хороший способ отслеживать обновленные и новые значения и обновлять представление только частично?
Вы всегда можете реализовать свою собственную таблицу, выступающую в качестве «материализованного представления». Это то, что вы должны были сделать до того, как MATERIALIZED VIEWбыли реализованы в Postgres 9.3 в любом случае.
Например, вы можете создать простой VIEW:
CREATEVIEW graph_avg_view ASSELECT xaxis, AVG(value)AS avg_val
FROM graph
GROUPBY xaxis;
И материализуйте результат в целом один раз или всякий раз, когда вам нужно начать все сначала:
(Или используйте SELECTинструкцию напрямую, не создавая VIEW.)
Затем, в зависимости от нераскрытых деталей вашего варианта использования, вы можете DELETE/ UPDATE/ INSERTизменить вручную.
Основной оператор DML с КТРОМ данных изменений для вашей таблицы , как является :
Предполагая , что никто другой не пытается писать в graph_avgодновременно (чтение это не проблема):
WITH del AS(DELETEFROM graph_avg t
WHERENOTEXISTS(SELECT1FROM 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
)INSERTINTO graph_avg t
SELECT*FROM graph_avg_view v
LEFTJOIN graph_avg t USING(xaxis)WHERE t.xaxis ISNULL;
Но это, скорее всего, должно быть оптимизировано.
Основной рецепт:
Добавьте timestampстолбец по умолчанию now()в вашу базовую таблицу. Давайте назовем это ts.
Если у вас есть обновления, добавить триггер , чтобы установить текущую метку времени с каждым обновлением , которое изменяет либо xaxisили value.
Создайте крошечную таблицу, чтобы запомнить временную метку вашего последнего снимка. Давайте назовем это mv:
CREATETABLE mv (
tbl text PRIMARYKEY, ts timestamp NOTNULLDEFAULT'-infinity');-- possibly more details
Создайте этот частичный многоколонный индекс:
CREATEINDEX 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 обновление материализованного представления означало блокировку всей таблицы и, следовательно, предотвращение каких-либо запросов к ней, и, если обновление занимало много времени, чтобы получить эксклюзивную блокировку (в то время как она ожидает запросов, использующих ее для завершения), она, в свою очередь, выполняет свою функцию. задерживает последующие запросы. Теперь это можно исправить с помощью ключевого слова CONCURRENTLY:
Однако в материализованном представлении должен существовать уникальный индекс. Вместо того, чтобы блокировать материализованное представление вверх, он создает временную обновленную версию, сравнивает две версии, затем применяет INSERT и DELETE к материализованному представлению, чтобы применить разницу. Это означает, что запросы могут по-прежнему использовать материализованное представление во время его обновления. В отличие от своей несовпадающей формы, кортежи не заморожены, и для них требуется VACUUMing из-за вышеупомянутых DELETE, которые оставят мертвые кортежи позади.
Это параллельное обновление все еще выполняет полный новый запрос (не инкрементный). Таким образом, CONCURRENTLY не экономит общее время вычислений, а просто минимизирует время, в течение которого ваше материализованное представление недоступно для использования во время его обновления.
На мгновение я был взволнован, пока не прочитал внимательно. it instead creates a temporary updated version of it...compares the two versions- Это означает, что временно обновленная версия все еще полностью рассчитана, тогда она применяет разницу к существующему представлению. По сути, я все еще делаю ВСЕ вычисления, но только во временной таблице.
user4150760
5
Ах, да, CONCURRENTLYэто не экономит общее время вычислений, оно просто минимизирует время, в течение которого ваше материализованное представление недоступно для использования во время его обновления.
Параллельное обновление (Postgres 9.4)
Хотя Postgres 9.4 и не является инкрементным обновлением, как вы просили, он предоставляет новую функцию одновременного обновления .
Процитирую документ…
Это параллельное обновление все еще выполняет полный новый запрос (не инкрементный). Таким образом, CONCURRENTLY не экономит общее время вычислений, а просто минимизирует время, в течение которого ваше материализованное представление недоступно для использования во время его обновления.
источник
it instead creates a temporary updated version of it...compares the two versions
- Это означает, что временно обновленная версия все еще полностью рассчитана, тогда она применяет разницу к существующему представлению. По сути, я все еще делаю ВСЕ вычисления, но только во временной таблице.CONCURRENTLY
это не экономит общее время вычислений, оно просто минимизирует время, в течение которого ваше материализованное представление недоступно для использования во время его обновления.