假设我有下表:
| id | claimed |
----------------
| 1 | NULL |
| 2 | NULL |
| 3 | NULL |
我可以执行此查询来更新其中一行,而不必先执行select。
UPDATE mytable SET claimed = [someId] WHERE claimed IS NULL LIMIT 1
但是,如果发生此查询的两个并发请求会发生什么。后面的请求是否可以覆盖第一个请求的值?我知道发生这种情况的可能性很小,但仍然存在。
答案 0 :(得分:0)
在事务UPDATE mytable SET claimed = [someId] WHERE claimed IS NULL LIMIT 1
中执行语句t1
会锁定相应的记录,并阻止任何其他事务t2
更新同一记录,直到事务t1
提交(或中止)。同时阻止了交易t2
; t2
一旦t1
提交(或中止),t2
继续,或者claimed
is null
在达到超时后自动中止。
授予mysql reference on internal locking methods - row level locking:
MySQL使用InnoDB表的行级锁定来同时支持 通过多个会话写入访问权限,使其适合 多用户,高度并发和OLTP应用程序。
和mysql reference on Locks Set by Different SQL Statements in InnoDB:
UPDATE ... WHERE ...在每条记录上设置一个独占的下一键锁定 搜索遭遇。但是,只需要索引记录锁定 对于使用唯一索引锁定行以搜索a的语句 独特的一行。
最后锁定在mysql引用InnoDB锁定记录锁的行为:
如果事务T1在行r上持有独占(X)锁,则为请求 从某个不同的事务T2中获取r上任一类型的锁 不能立即授予。相反,事务T2必须等待 事务T1释放其对行r的锁定。
只要这两个查询在不同的事务中运行,两个查询就不会获取相同的记录。
请注意,完整记录已被锁定,以便其他事务的其他更新操作被阻止,即使它们会更新相应记录的其他属性。
我尝试使用SequelPro,您可以尝试使用任何您想要的客户端,如下所示:
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
,其中(先前已启动的)查询应该在此处
现在已经完成;执行mytable
; claim=15
时,一条记录现在应该有claim=16
,
另一个应该有db.test.find(
{
'values': {
$elemMatch: {
$elemMatch: {
$in: ['a']
}
}
}
},
{
"values.$": 1
}
)
。