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 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
。
提前谢谢你。
【问题讨论】:
【参考方案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”排他锁分析