假设我有下表:

| id | claimed |
----------------
| 1  | NULL    |
| 2  | NULL    |
| 3  | NULL    |

我可以执行这个查询来准确地更新(任何)一行,而不必先执行select。
UPDATE mytable SET claimed = [someId] WHERE claimed IS NULL LIMIT 1
但是,如果此查询的两个并发请求发生时会发生什么情况。以后的请求是否可以重写第一个请求的值?我知道发生这种事的可能性很小,但仍然存在。

最佳答案

在事务UPDATE mytable SET claimed = [someId] WHERE claimed IS NULL LIMIT 1中执行语句t1将锁定相应的记录,并防止任何其他事务t2在事务t1提交(或中止)之前更新同一记录。同时,事务t2被阻塞;t2在提交(或中止)后继续,或在达到超时时自动中止。
授予:
mysql对innodb表使用行级锁定来支持同时
通过多个会话进行写访问,使它们适合于
多用户、高并发和oltp应用程序。
以及mysql reference on internal locking methods - row level locking
更新。。。哪里。。。在每个记录上设置独占的下一个密钥锁
搜索遇到了。但是,只需要索引记录锁
对于使用唯一索引锁定行以搜索
唯一的行。
最后,mysql中的锁定行为引用innodb locking for record locks:
如果事务T1持有R行的独占(X)锁,则请求
对于r上任意类型的锁,从某个不同的事务t2
无法立即授予。相反,事务t2必须等待
事务T1释放对行R的锁。
因此,只要这两个查询在不同的事务中运行,这两个查询就不会获取相同的记录。
注意,完整的记录被锁定,这样其他事务的其他更新操作被阻止,即使它们会更新相应记录的其他属性。
我用Sequelpro试过了,你可以和任何你想要的客户一起试,如下所示:
确保mytable至少包含两个t1记录。
打开两个连接窗口/终端;我们称它们为t2
claimedis null
c1中,执行以下两个命令:c2
c1中,执行类似的命令(注意
start transaction;UPDATE mytable SET claimed = 15 WHERE claimed IS NULL LIMIT 1; #No commit so far!):c2
窗口应通知您它正在工作(即等待
要完成的查询)。
切换到窗口claimed并执行命令start transaction; UPDATE mytable SET claimed = 16 WHERE claimed IS NULL LIMIT 1; # Again, no commit so far
切换到窗口c2,其中(先前启动的)查询应
现在已经完成;执行c1
当查看commit;时,一条记录现在应该有c2
另一个应该有commit

关于mysql - 使用WHERE IS NULL和LIMIT执行更新时可能发生冲突?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/41634516/

10-09 22:09