ВЫБЕРИТЕ обновленные строки во время одновременных обновлений - PullRequest
0 голосов
/ 21 февраля 2020

Предположим, у меня есть набор кода, который создает транзакцию, выбирает, удаляет и повторно создает ресурс и фиксирует его, чтобы обновить его:

  1. BEGIN
  2. SELECT * FROM "product" WHERE (code = 'A') FOR UPDATE
  3. DELETE FROM "product" WHERE "product"."id" = 1
  4. INSERT INTO "product" ("id","code","price") VALUES (1,'A',3000) // Updated price
  5. COMMIT

Теперь представьте, что этот код запустить дважды, одновременно и пытаясь изменить одну и ту же строку:

TX1 - BEGIN
TX1 - SELECT product
TX1 - DELETE product

TX2 - BEGIN
TX2 - SELECT product - This blocks until TX1 has been committed

TX1 - INSERT updated product
TX1 - COMMIT

TX2 - SELECT product - Error occurs here...

Если я использую уровень транзакции ReadCommitted, я получаю 0 возвращаемых строк. Обратите внимание, что TX2 было создано после того, как продукт был УДАЛЕН в TX1.

Я не могу использовать Serializable или RepeatableRead, так как строка изменилась, я получаю pq: could not serialize access due to concurrent update.

Есть ли способ получить блок SELECT in TX2 до тех пор, пока TX1 не будет завершен, а затем SELECT the новая обновленная строка ? Я неправильно использую SELECT...FOR UPDATE?

1 Ответ

1 голос
/ 24 февраля 2020

Поведение и причины того, как уровни изоляции работают в PostgreSQL, обобщены в их документации: https://www.postgresql.org/docs/9.5/transaction-iso.html#XACT -READ-COMMITTED ). С

С моей точки зрения, важно то, что PostgreSQL использует оптимистическую блокировку c для обработки транзакций. Когда транзакция запущена, делается снимок, и он работает с этим. Ручное использование SELECT ... FOR UPDATE означает использование блокировки pessimisti c для строки.

Теперь, когда вы используете RepeatableRead, база данных обещает, что изоляция транзакции гарантирована.

Когда TX1 пессимистически блокирует строку, TX2 ожидает, что будет дальше. Если TX1 откатывается, то TX2 может продолжаться. Но поскольку TX1 фиксирует, то TX2 не имеет других возможностей, кроме отката и выдает исключение, чтобы выполнить обещание уровня изоляции.

Здесь вы можете повторно выполнить запрос SQL который увидит обновленную версию состояния базы данных.

Так что для ответа на ваш вопрос

Есть ли способ иметь SELECT в блоке TX2, пока TX1 не будет завершен , а затем ВЫБЕРИТЕ новую обновленную строку?

Да, используйте RepeatableRead, а когда запрос TX2 не удастся, повторите запрос еще раз.

...