SELECT FOR UPDATE在MySQL中保存整个表而不是逐行

时间:2014-03-07 05:16:08

标签: mysql select transactions sql-update

我将有多个客户端将数据输入数据库,我必须确保交易不会混杂在一起。

我在文档中读到START TRANSACTIONSELECT ... 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

提前谢谢。

1 个答案:

答案 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将锁定一系列行。