Уровень изоляции, необходимый для надежного уменьшения / увеличения на одном поле - PullRequest
3 голосов
/ 22 января 2011

Представьте, что у нас есть следующая таблица:

+----+---------+--------+
| id | Name    | Bunnies|
+----+---------+--------+
|  1 | England |   1000 |
|  2 | Russia  |   1000 |
+----+---------+--------+

И у нас есть несколько пользователей, удаляющих кроликов в течение определенного периода времени, например, 2 часа. (Таким образом, минимум 0 кроликов, максимум 1000 кроликов, кролики возвращаются, а не добавляются пользователями)

Я использую два основных запроса транзакций, таких как

BEGIN;
  UPDATE `BunnyTracker` SET `Bunnies`=`Bunnies`+1 where `id`=1;
COMMIT;

Когдакто-то возвращает зайчика и,

BEGIN;
  UPDATE `BunnyTracker` SET `Bunnies`=`Bunnies`-1 where `id`=1 AND `Bunnies` > 0;
COMMIT;

когда кто-то пытается взять зайчика.Я предполагаю, что эти запросы будут реализовывать некоторую атомарность под капотом

Крайне важно, чтобы пользователи не могли брать больше зайчиков, чем в каждой стране, (т.е. -23 зайчика, если одновременно работают 23 пользователя)

Моя проблема в том, как мне сохранить безопасность ACID в этом случае, при этом имея возможность одновременно добавлять / увеличивать / уменьшать поле кроликов, оставаясь в пределах (0-1000), я мог бы установитьуровень изоляции до сериализованного, но я переживаю, что это снизит производительность.

Какие-нибудь советы?Заранее спасибо

Ответы [ 2 ]

2 голосов
/ 23 января 2011

Я полагаю, что вам необходимо реализовать некоторую дополнительную логику, чтобы предотвратить одновременное чтение обеими транзакциями increment и decrement одного и того же начального значения.

В существующем случае, если Bunnies = 1, у вас могут быть одновременные транзакции увеличения и уменьшения, которые оба читают начальное значение 1. Если приращение затем завершается первым, его результаты будут игнорироваться, поскольку декремент ужепрочитайте начальное значение 1 и уменьшите значение до 0. Независимо от того, какая из этих операций завершится последней, вы фактически отмените другую операцию.

Чтобы решить эту проблему, необходимо реализовать блокировку чтения с помощью SELECT ... FOR UPDATE,как описано здесь .Например:

BEGIN;
  SELECT `Bunnies` FROM `BunnyTracker` where `id`=1 FOR UPDATE;
  UPDATE `BunnyTracker` SET `Bunnies`=`Bunnies`+1 where `id`=1;
COMMIT;
1 голос
/ 22 января 2011

Хотя пользователям кажется, что в БД одновременно происходит несколько транзакций, они фактически являются последовательными. (Например, записи записываются в журналы повторов / транзакций по одному).

Будет ли поэтому работать для вас, чтобы поставить ограничение на таблицу "bunnies> = 0" и отловить ошибку транзакции, которая пытается нарушить это ограничение?

...