Postgres, MVCC и Locking

8

У меня есть ряд операторов SQL, которые выглядят следующим образом:

BEGIN;
SELECT counter FROM table WHERE id=X FOR UPDATE;
REALLY COMPLEX QUERY;
UPDATE table SET counter=Y WHERE id=X;
END;

Я хотел бы запретить считывание счетчика, пока я пересчитываю его значение, но, согласно документам Postgres, «блокировки на уровне строк не влияют на запросы данных; они блокируют только записи в одну и ту же строку».

Вопросов:

  1. Какой смысл "исключительной" блокировки строк, если она не предотвращает чтение? Только для того, чтобы другие транзакции не блокировали акции?
  2. Если я прочитаю строку с помощью SELECT ... FOR SHARE, то достигнет ли это того же эффекта, что и «исключительная» блокировка?
  3. Можно ли отключить MVCC для таблицы / схемы / базы данных и разрешить запись на место?
Kevin
источник

Ответы:

5

1) Любой другой сеанс будет считывать данные, измененные вашей транзакцией, как это было до вашего оператора «BEGIN», пока ваша транзакция не была зафиксирована. Как только ваша транзакция будет зафиксирована, будет прочитано новое значение счетчика. Дело в том, что другие не должны ждать и всегда будут видеть согласованную базу данных.

на 2), 3) Почему бы вам не попробовать это с «ACCESS EXCLUSIVE»? (см. http://www.postgresql.org/docs/current/static/explicit-locking.html )

РЕДАКТИРОВАТЬ: Если вам не нравится блокировка всей таблицы с помощью блокировки «ACCESS EXCLUSIVE», вы также можете использовать «Консультативную блокировку» (см. Раздел 13.3.4 в ссылке выше).

JP
источник
1

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

Проверьте документы на утверждение UPDATE. Чтобы ответить на ваш вопрос об исключительной блокировке строк, нужно предотвратить несогласованность данных при одновременном обновлении. Читатели всегда получают согласованное представление о базе данных.

Ketema
источник
1

В предоставленном вами примере кода все чтения этой строки будут видеть старое значение, пока эта транзакция не будет зафиксирована.

1) Рассмотрим пример кода, запускаемый дважды одновременно с одинаковым значением X. Если экземпляр A запущен, select ... for updateон блокирует эту строку до фиксации. Экземпляр B, выполняющий то же самое, заблокирует попытку получить блокировку таким же образом. Только когда A фиксирует, B может продолжить. Затем B получит значение A, оставшееся в его последнем обновлении.

Если Yзависит от того, какое значение select ... for updateпрочитал запрос или другое чтение его в «сложной» части, то это будет иметь такой же эффект, как если бы они выполнялись последовательно - вы не получите условие гонки, при котором один из результатов будет отбрасываются.

Если Yэто просто результат сложных запросов в середине, то выполнение его параллельно без select ... for updateтого, чтобы они оба выполняли одно и то же обновление.

2) for shareпозволит другим выборам в этой строке продолжить, но заставит что-либо еще попытаться select ... for updateили update ...заблокировать, пока это не будет сделано. Если образец кода был запущен дважды, они одновременно зашли бы в тупик при достижении updateоператора, в результате чего один из них был прерван.

3) Нет. Делать это опасно, поскольку читатель может прочитать наполовину обновленное поле. (Или прочитайте оставшуюся часть поля, обновленного после начала чтения.) Это также может нарушить согласованность, показывая читателю значение, обновленное после начала его транзакции.

MaHuJa
источник