Как предотвратить аномалии обновления с несколькими клиентами, выполняющими неатомарные вычисления одновременно в PostgreSQL? - PullRequest
0 голосов
/ 11 февраля 2019

Я использую три экземпляра PostgreSQL с использованием репликации (1 главный, 2 подчиненных), к которым обращаются два отдельных сервера:

  • Первый (неэкспонированный) сервер в основном выполняет итерации по каждой строке в конкретной таблицеи постоянно обновляет определенные столбцы (ресурсы) каждый тик (на основе скорости производства этих ресурсов) для каждого пользователя.
  • Второй сервер - это общедоступный API, который предоставляет различные функции, такие как расходование определенного количества этих ресурсов.

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

const resources = await repository.findById(1337);
// some complex computation
resources.iron = computeNewIron(resources.iron);
await repository.save(resources);

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

Моя проблема в том, что я не просто пишу«простой» атомарный запрос такой UPDATE table SET iron = iron + 42 WHERE id = :id.Библиотека ORM внутренне использует прямое назначение, которое не ссылается на соответствующие столбцы самостоятельно, что приводит к чему-то похожему на UPDATE table SET iron = 123 WHERE id = :id, где сумма была вычислена ранее.

Я могу только предположить, что можно предотвратитьупомянутая аномалия, если я использую написанные вручную запросы, которые увеличивают / уменьшают значения атомарно с помощью собственных ссылок.Я хотел бы знать, какие другие варианты могут облегчить проблему.Должен ли я обернуть мой SELECT / Computation / UPDATE в транзакции?Достаточно ли этого?

1 Ответ

0 голосов
/ 11 февраля 2019

Ваш вопрос немного неясен, но если ваша транзакция охватывает несколько операторов, но при этом должна иметь согласованное состояние базы данных, есть в основном два варианта:

  1. Использовать пессимистическую блокировку: когда вы читаете значения из базы данных, сделайте это с SELECT ... FOR UPDATE.Затем строки блокируются на время вашей транзакции, и никакая параллельная транзакция не может их изменить.

  2. Использовать оптимистическую блокировку: начните транзакцию с уровня изоляции REPEATABLE READ.Затем вы видите непротиворечивый снимок базы данных на весь период вашей транзакции.Если кто-то еще изменит ваши данные после того, как вы их прочитаете, ваш UPDATE вызовет ошибку сериализации , и вам придется повторить транзакцию.

Оптимистическая блокировкалучше, если конфликты редки, а пессимистическая блокировка предпочтительнее, если конфликты вероятны.

...