我将有多个客户端将数据输入数据库,我必须确保交易不会混杂在一起。
我在文档中读到START TRANSACTION
和SELECT ... FOR UPDATE
锁定它读取的每一行:
SELECT ... FOR UPDATE读取最新的可用数据,在其读取的每一行上设置独占锁。因此,它设置搜索的SQL UPDATE将在行上设置的相同锁。
请参阅https://dev.mysql.com/doc/refman/5.0/en/innodb-locking-reads.html
所以我登录了一个客户端并键入了这些语句:
START TRANSACTION;
SELECT * FROM productMacAddress WHERE status='free' limit 8 FOR UPDATE;
暂停此处以获取第二个客户端....
UPDATE productMacAddress SET status='testing1' WHERE status='free' LIMIT 8;
COMMIT;
在另一个客户端,我输入:
START TRANSACTION;
SELECT * FROM productMacAddress WHERE status='free' limit 4 FOR UPDATE;
UPDATE productMacAddress SET status='testing2' WHERE status='free' LIMIT 4;
COMMIT;
但是在完成第一个客户端之前,我无法SELECT
表中的任何内容。为什么会这样?文档说明它应该逐行锁定,特别是因为我LIMIT 8
。
提前谢谢。
答案 0 :(得分:1)
InnoDB表的默认隔离级别是可重复读取。当此隔离级别处于活动状态时,我们会收到以下行为(引自:https://dev.mysql.com/doc/refman/5.5/en/set-transaction.html):
对于锁定读取(使用FOR UPDATE或LOCK IN SHARE MODE选择), UPDATE和DELETE语句,锁定取决于是否 statement使用具有唯一搜索条件的唯一索引,或者a 范围类型搜索条件。对于具有唯一搜索的唯一索引 条件,InnoDB只锁定找到的索引记录,而不是间隙 在它之前。对于其他搜索条件,InnoDB锁定索引范围 扫描,使用间隙锁或下一键(间隙加索引记录)锁定 阻止其他会话插入范围所涵盖的空白。
换句话说:您是否可以尝试在SELECT的WHERE条件中使用主键?例如,而不是:
START TRANSACTION;
SELECT * FROM productMacAddress WHERE status='free' limit 8 FOR UPDATE;
尝试:
START TRANSACTION;
SELECT * FROM productMacAddress WHERE id=10 FOR UPDATE;
如果id是主键。任何其他具有唯一索引的列也可以使用。在WHERE子句中使用非唯一列时,InnoDB将锁定一系列行。