我正在开发的应用程序中存在潜在的竞争状况,我想在查询中考虑和避免这种情况。
总结应用程序流程…
entries
INSERT INTO entries ( name, email ) VALUES ( 'Foo Bar', 'foo@example.com' );
prizes
SELECT id FROM prizes WHERE various_time_conditions = 'met' AND id NOT IN ( SELECT prize_id FROM entries );
UPDATE entries SET prize_id = [prize id] WHERE id = [entry id];
由于每个奖项只能颁发一次,因此我需要消除比赛条件的任何可能性,在这种情况下,另一个过程可以查询奖项表并更新上述步骤2和3之间的条目表。
我一直在做一些研究,发现了大量关于事务的信息(我的所有表都使用InnoDB)以及使用MySQL的SELECT ... FOR UPDATE语法,但是我对于哪一个最适合我的解决方案感到困惑。
SELECT ... FOR UPDATE
您将要锁定奖金记录。因此,如果您不打算使用winner_id之类的东西,请在奖品表上添加一些可用性标志(也许具有默认值)。像这样的东西:
SELECT id FROM prizes WHERE ... AND available = 1 FOR UPDATE
然后,如果您确实分配奖品,请设置可用性:
UPDATE prizes SET available = 0 WHERE id = ...
当然,您需要将其包装在一个事务中。
确保 每次 检查奖金是否可用时,都将其添加AND available = 1 FOR UPDATE到查询中,因为SELECT没有的a不会FOR UPDATE等待锁定。
AND available = 1 FOR UPDATE
SELECT
FOR UPDATE