Что произойдет, если два процесса попытаются ОБНОВИТЬ МАТЕРИАЛИЗОВАННЫЙ ВИД одновременно?

13

Согласно документам:

В то же время Обновите материализованное представление, не блокируя параллельные выборки в материализованном представлении. (...)

... ДРУГОЕ СОДЕРЖАНИЕ ...

Даже с этой опцией только один REFRESH одновременно может работать с любым материализованным представлением .

У меня была функция, которая проверяла время последнего обновления для МАТЕРИАЛИЗИРОВАННОГО ВИДА и, если прошло более 60 секунд, оно обновило бы его.

Однако что произойдет, если я попытаюсь обновить материализованное представление из двух отдельных процессов одновременно? Будут ли они в очереди или они вызовут ошибку?

Есть ли способ определить, когда обновляется МАТЕРИАЛИЗИРОВАННЫЙ ВИД, и, следовательно, избегать его касания?

В настоящее время я прибегал , чтобы заполнить запись таблицы перед обновлением (настройка refreshingв true) , а затем установить его в , falseкогда процесс завершен.

EXECUTE 'INSERT INTO refresh_status (last_update, refreshing) 
         VALUES (clock_timestamp(), true) RETURNING id') INTO refresh_id;
EXECUTE 'REFRESH MATERIALIZED VIEW CONCURRENTLY my_mat_view';
EXECUTE 'UPDATE refresh_status SET refreshing=false WHERE id=$1' USING refresh_id;

Затем, всякий раз, когда я вызываю эту процедуру, я проверяю самое последнее last_updateи его refreshingзначение. Если refreshingэто правда, то не пытайтесь обновить материализованное представление.

EXECUTE 'SELECT 
           extract(epoch FROM now() - (last_update))::integer, 
           refreshing
         FROM refresh_status
         ORDER BY last_update DESC
         LIMIT 1' INTO update_seconds_ago, refreshing;

IF(updated_seconds_ago > 60 AND refreshing = FALSE) THEN
  -- the refresh block above
END IF;

Тем не менее, я не уверен, что флаг обновления обновляется синхронно (я имею в виду, что он действительно ожидает завершения обновления)

Это рациональный подход или я что-то здесь упускаю?

ffflabs
источник

Ответы:

13

Как уже упоминалось в этом ответе , « REFRESH MATERIALIZED VIEW CONCURRENTLYберет EXCLUSIVEзамок» на столе. Следуя следам крошки к документации, мы можем прочитать, что EXCLUSIVEблокировка таблицы «допускает только одновременные ACCESS SHAREблокировки, т. Е. Могут выполняться только чтения из таблицы». В том же абзаце мы видим, что « EXCLUSIVEконфликтует с ... EXCLUSIVE», что означает, что другой REFRESH MATERIALIZED VIEW CONCURRENTLYоператор, который запрашивает такую ​​же EXCLUSIVEблокировку, должен будет ждать, пока не будет снята более ранняя EXCLUSIVEблокировка.

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

mustaccio
источник
PS: как вы думаете, имеет ли смысл хранить эту вспомогательную таблицу, чтобы сообщать параллельным (не рассчитанным на каламбур) попыткам, что MAT VIEW занят и поэтому его следует оставить в покое, даже если кажется, что он нуждается в обновлении?
ffflabs
Это вопрос мнения; если вы думаете, что это помогает, вы, конечно, можете сохранить свою логику. Тем не менее, обратите внимание, что ваша функция зависит от условий гонки и поэтому не является надежной на 100%.
Мустаччо
Как вы думаете, целесообразно ли проверить pg_locks и посмотреть, есть ли один, ссылающийся на представление мата?
ffflabs
Опять же, возможно состояние гонки: есть вероятность, что между вами будет проверена блокировка pg_locksи начнется обновление. Правильный способ устранения конфликтов блокировки - установить таймаут и обработать ошибку.
Мустаччо
3

Как отметил Мустаччо , этот вопрос значительно совпадает с блокировками представлений Postgres Refresh .

Однако, хотя принятый ответ на этот вопрос имеет ссылку, которая отвечает на этот вопрос, ответ на этот вопрос напрямую не включен в этот вопрос.

Итак, чтобы быть конкретным: согласно странице руководства PostgreSQL по явной блокировке (ссылка на страницу текущей версии, для PostGres 10), REFRESH MATERIALIZED VIEW CONCURRENTLYпринимает EXCLUSIVEблокировку. EXCLUSIVEЗамок , кажется, блокирует все другие замки , за исключением ACCESS SHARE - что включает в себя другие EXCLUSIVEзамки.

Таким образом, второй REFRESH MATERIALIZED VIEW CONCURRENTLYзапрос в том же представлении будет ожидать блокировки, полученной первым, для освобождения.

RDFozz
источник
Спасибо. Я все еще отмечал ответ @ mustaccio как принятый, так как он отредактировал свой текст, чтобы быть более конкретным для моего вопроса.
ffflabs
0

Благодаря ответам mustaccio и RDFozz я наконец понял, что REFRESH ... CONCURRENTLYвзятие эксклюзивной блокировки является причиной, по которой в документации PostgreSQL говорится :

Даже с этой опцией только один REFRESH одновременно может работать с любым материализованным представлением .

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

Блокировка, полученная во время этой операции, предотвратит любую операцию, кроме чтения из МАТЕРИАЛИЗИРОВАННОГО ВИДА. Дальнейшие попытки обновить материализованное представление во время работы команды REFRESH ... CONCURRENTLY будут стоять в очереди до тех пор, пока не будет снята первая блокировка.

ffflabs
источник