寻找在mySQL中锁定表的正确方法

时间:2012-01-17 22:47:21

标签: mysql

我遇到的情况是,在任何其他事务可以查看给定表之前,特定事务必须更新数据库。具体来说,有一个奖励机制,其中奖品数量有限,我担心如果两个请求几乎同时到达,第二个请求可能会发现奖品仍然可用,因为第一个请求没有时间它需要将其标记为不可用。

我正在查看lock tables的文档,我不清楚发生了什么,因为测试此功能非常困难(因为它需要两个请求同时到达),我是希望得到一些建议。

我的需求非常简单。我只需要锁定一个表,而其他所有表都可以用来开展业务。

**request 1**:
lock prizes;
select from prizes;
mark prize as unavailable;
unlock prizes;

simultaneous **request n**
find the prizes table locked and wait for it to unlock //this is not critical, so long as they can just fail gracefully
select [no prize available]

正如我所说,这个数据库中的其他表完全不受我的锁影响是非常重要的,我从文档中得到的结论是,当我锁定一个表时,选择另一个表将产生一个错误,表示“其他表”没有锁定“...我可能没有正确理解这一点,因为这看起来很愚蠢,但只需要确保锁定奖品不会影响其他任何事情。

TIA

3 个答案:

答案 0 :(得分:1)

您应该只需LOCK prizes WRITE即可获得所需的语义。由于锁定一个或多个表会阻止您访问未锁定锁定持续时间的任何表,因此您还需要锁定 - 无论是读取还是写入 - 您需要执行的所有其他表"将奖品标记为不可用"步骤,如果有的话。

请注意,如果您打算使用别名访问表,则还需要在LOCK语句中提供相同的别名。文档中介绍了此主题,但我明确提到它,因为它可能会被忽略。

答案 1 :(得分:1)

这是交易的完美案例。请注意,您的表需要是InnoDB才能使用。

start transaction;
select [your_fields] from [prizes_table] WHERE [your_where] FOR UPDATE;
// if is a valid recipient and prize gets taken:
update prizes set available=0 where id=[used_prize_id];
commit;

这应该完全符合你的期望。

答案 2 :(得分:0)

如果您只需要锁定记录,则可以使用select ... for update设置autocommit=0并使用该交易。

这样第一个请求选择记录并设置锁定,然后第二个请求被阻塞,直到第一个请求提交(或回滚)事务或等待超过了timout