单个字段上可靠的de / increment所需的隔离级别

时间:2011-01-22 19:59:58

标签: mysql concurrency transactions isolation-level atomicity

想象一下,我们有一个表格如下,

+----+---------+--------+
| 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 个答案:

答案 0 :(得分:2)

我认为您需要实现一些额外的逻辑来防止并发incrementdecrement事务都读取相同的初始值。

就目前而言,如果Bunnies = 1,你可以同时增加和减少事务,它们都读取初始值1.如果增量首先完成,其结果将被忽略,因为减量已经读取了初始值值为1并将值递减为0.无论这些操作最后完成哪一个都将有效地取消其他操作。

要解决此问题,您需要使用SELECT ... FOR UPDATE实现锁定读取 描述here。例如:

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

答案 1 :(得分:1)

虽然看起来用户在数据库中同时发生多个事务,但它们实际上是顺序的。 (例如,条目一次一个地写入重做/事务日志)。

因此,您是否可以在表格“bunnies> = 0”上设置约束并捕获试图破坏该约束的交易失败?