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

Posted

技术标签:

【中文标题】SELECT FOR UPDATE 在 MySQL 中保存整个表,而不是逐行保存【英文标题】:SELECT FOR UPDATE holding entire table in MySQL rather than row by row 【发布时间】:2014-04-10 03:03:41 【问题描述】:

我将有多个客户将数据输入数据库,我必须确保事务不会混合。

我在文档中读到 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】:

InnoDB 表的默认隔离级别是可重复读取。当此隔离级别处于活动状态时,我们会得到以下行为(引用自:https://dev.mysql.com/doc/refman/5.5/en/set-transaction.html):

对于锁定读取(SELECT with FOR UPDATE 或 LOCK IN SHARE MODE), UPDATE 和 DELETE 语句,锁定取决于是否 语句使用具有唯一搜索条件的唯一索引,或 范围类型的搜索条件。对于具有唯一搜索的唯一索引 条件,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 将锁定一系列行。

【讨论】:

有些复杂的情况是:当“SELECT FOR UPDATE”后跟WHERE语句如:“WHERE (indexed_key= value1 or indexed_key=value2)”时,InnoDB会锁定整个表再次而不是这两行。这是为什么呢?

以上是关于SELECT FOR UPDATE 在 MySQL 中保存整个表,而不是逐行保存的主要内容,如果未能解决你的问题,请参考以下文章

mysql事务,select for update,及数据的一致性处理

数据库:Mysql中“select ... for update”排他锁分析

MySQL的SELECT.FOR UPDATE究竟起啥作用

数据库:Mysql中“select ... for update”排他锁分析

MySQL的SELECT ...for update

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