MySQL INSERT SELECT WHERE竞争条件

时间:2013-05-03 06:09:04

标签: mysql concurrency innodb race-condition

我正在开发一个票务系统,在这个系统中,用户可以在申请之前立即托管大量的门票(基本上所有门票都没有缺货)。这些票证显示给用户,他们可以选择他们想要声明的票证。

如果两个用户试图同时托管相同的门票并且没有足够的门票,则此托管系统可能会引入竞争条件,如:

门票离开:1

用户A点击页面,检查剩余的票数。还剩1张票 用户B点击页面,检查剩余的票数。剩下1张票

由于他们都有一张票,所以他们都会托管票,让门票留下-1。

我想尽可能避免锁定,并且想知道是否带有子查询的语句如

INSERT INTO ticket_escrows (`ticket`,`count`) 
SELECT ticket,tickets_per_escrow FROM tickets WHERE tickets.total > (
    COALESCE(
        SELECT SUM(ticket_escrows.count) FROM ticket_escrows 
        WHERE ticket_escrows.ticket = tickets.id
        AND ticket_escrows.valid = 1
    ,0)
    +
    COALESCE(
        SELECT SUM(ticket_claims.count) 
        FROM ticket_claims
        WHERE ticket_claims.ticket = tickets.id
    ,0)
)

将是原子的,允许我在没有锁定的情况下防止竞争条件。

具体来说,我想知道上述查询是否会阻止以下情况发生:

Max tickets: 50 Claimed/Escrowed tickets: 49
T1: start tx -> sums ticket escrows --> 40
T2: start tx -> sums ticket escrows --> 40
T1: sums ticket claims --> 9
T2: sums ticket claims --> 9
T1: Inserts new escrow since there is 1 ticket left --> 0 tickets left
T2: Inserts new escrow since there is 1 ticket left --> -1 tickets left

我正在使用InnoDB。

2 个答案:

答案 0 :(得分:2)

回答你的问题“如果带有子查询的语句......将是原子的”:在你的情况下,是的。

只有封闭在事务中才会是原子的。由于您声明正在使用InnoDB,因此即使使用子查询,查询也是一个SQL语句,因此在事务中执行。引用documentation

  

在InnoDB中,所有用户活动都发生在事务中。如果启用了自动提交模式,则每个SQL语句都会单独形成一个事务。

     

...如果语句返回错误,则提交或回滚行为取决于错误。

另外,isolations levels很重要。

  

就SQL:1992事务隔离级别而言,默认的InnoDB级别是REPEATABLE READ

REPEATABLE READ 对您来说可能不够,具体取决于程序的逻辑。它可以防止事务写入另一个事务读取的数据,直到读取事务完成,然而phantom reads成为可能。检查SET TRANSACTION 以了解如何更改隔离级别。


回答第二个问题“如果上述查询会阻止以下情况发生......”:在具有SERIALIZABLE隔离级别的事务中,它不会发生。我认为默认级别在您的情况下也应该是安全的(假设tickets.total没有改变),但我更愿意让某人确认。

答案 1 :(得分:1)

你真的留下了很多关于你希望如何工作的信息,这就是为什么你没有得到更多/更好的答案。

票务是一个权衡问题。如果你向某人展示有10张门票,你要么立即让所有10张门票对其他人都不可用(这对其他人都不好),要么你不这样做,这意味着这个人可能会订购一张其他人抢购的门票。他们决定拿哪张票。 “托管”系统并没有真正帮助解决问题,因为它只是将购买门票的问题转移到托管的门票上。

在您没有锁定其他人的期间,最佳做法是以这样的方式制作SQL:如果其他人在您处理数据时修改了数据,则更新或插入将失败。这可以像每次更改行时在行中递增计数器一样简单,并在UPDATE语句的WHERE子句中使用该计数器(加上主键)。如果计数器发生变化,则更新失败,您知道自己已经输掉了比赛。

我不明白你想要发生什么,或者你的数据结构足以给你更多的建议。