如何锁定尚不存在的InnoDB行?

xLi*_*ite 17 mysql innodb locking rowlocking database-locking

我如何保证我可以搜索我的数据库中是否存在用户名,然后将该用户名作为新行插入数据库而不在SELECTINSERT语句之间进行任何拦截?

几乎就像我锁定了一个不存在的行.我想用用户名"Foo"锁定不存在的行,这样我现在可以检查它是否存在于数据库中并将其插入到数据库中(如果它尚不存在而没有任何中断).

我知道使用LOCK IN SHARE MODEFOR UPDATE存在,但据我所知,这只适用于已经存在的行.我不知道在这种情况下该怎么做.

Ran*_*eed 19

如果有索引username(应该是这种情况,如果没有,则添加一个,最好是UNIQUE一个),然后发出一个SELECT * FROM user_table WHERE username = 'foo' FOR UPDATE;将阻止任何并发事务创建此用户(以及"上一个"和"下一个")在非唯一索引的情况下可能的值).

如果找不到合适的索引(满足WHERE条件),则无法进行有效的记录锁定,整个表将被锁定*.

此锁定将一直持续到发布的事务结束SELECT ... FOR UPDATE.

有关此主题的一些非常有趣的信息可以在这些手册页中找到.

*我说效率很高,因为实际上记录锁实际上是对索引记录的锁定.如果找不到合适的索引,则只能使用默认的聚簇索引,并且它将被完全锁定.

  • 如果我为没有索引的内容添加新行怎么办?它是否在没有索引的情况下锁定整个表? (2认同)
  • 是的,我忘了提。如果找不到适合记录锁的索引,则整个表将变为只读。 (2认同)
  • 所有这些都取决于在“ SELECT ... FOR UPDATE”语句中使用的过滤条件。如果可以使用索引(请考虑使用“ EXPLAIN”),则该索引将用于锁定。如果不是,则整个表将生效。MySQL的FOR UPDATE语句非常保守。无论表中的索引如何,此操作都是100%安全的。请记住,当您认为只锁定一行时,您可能会锁定整个表。 (2认同)
  • 这似乎对我不起作用。我通过启动两个事务进行测试,在事务1中执行“SELECT ... FOR UPDATE”,然后在事务2中插入一条记录,发现事务2没有被阻塞。到目前为止,我找到的唯一解决方案是在每个 DELETE 语句前面加上适当的 INSERT 语句,以确保在执行 DELETE 之前存在一行(因此可以锁定)。FWIW,我的数据库处于 READ_COMMITTED 模式。 (2认同)

Bin*_*rus 16

虽然上面的答案是正确的,因为SELECT ... FOR UPDATE将阻止并发会话/事务插入相同的记录,这不是完整的事实.我目前正在解决同样的问题,并得出结论,SELECT ... FOR UPDATE在这种情况下几乎无用,原因如下:

并发事务/会话也可以在相同的记录/索引值上执行SELECT ... FOR UPDATE,并且MySQL将很乐意立即接受(非阻塞)并且不会抛出错误.当然,只要其他会话完成,您的会话也不能再插入记录了.您和其他会话/交易也不会获得有关情况的任何信息,并认为他们可以安全地插入记录,直到他们真正尝试这样做.尝试插入然后导致死锁或重复键错误,具体取决于具体情况.

换句话说,SELECT ... FOR UPDATE阻止其他会话插入相应的记录,但即使你做了一个SELECT ... FOR UPDATE并且找不到相应的记录,很可能你实际上不能插入该记录.恕我直言,这使得"第一个查询,然后插入"方法无用.

问题的原因是MySQL没有提供任何方法来真正锁定不存在的记录.两个并发会话/事务可以同时锁定不存在的记录"FOR UPDATE",这实际上是不可能的,这使得开发变得更加困难.

解决这个问题的唯一方法似乎是使用信号量表或在插入时锁定整个表.有关锁定整个表或使用信号量表的更多参考,请参阅MySQL文档.

只差我2美分......

  • 另一种选择,虽然不一定在所有情况下都是理想的,但跳过 SELECT ... FOR UPDATE 并只执行 INSERT 然后处理产生的重复键错误(根据我的经验,当插入是第一个执行的操作时,这更加一致)。我敢肯定会有性能损失,但是,在很多情况下,与执行的其他操作相比,它可以忽略不计,并且可以为您省去必须创建互斥表的麻烦。 (3认同)

Yan*_*hao 8

锁定不存在的记录在MySQL中不起作用.有几个关于它的错误报告:

一种解决方法是使用互斥表,在插入新记录之前将锁定现有记录.例如,有两个表:卖家和产品.卖家有很多产品,但不应该有任何重复的产品.在这种情况下,卖家表可以用作互斥表.在插入新产品之前,将在卖方记录上创建锁定.使用此附加查询,可以保证在任何给定时间只有一个线程可以执行操作.没有重复.没有死锁.