只选择未锁定的行mysql

时间:2013-11-20 09:07:12

标签: mysql transactions

我通过以下查询

在一个交易中锁定了一行
START TRANSACTION;

SELECT id FROM children WHERE id=100 FOR UPDATE;

在另一个交易中,我有一个查询如下

START TRANSACTION;

SELECT id FROM children WHERE id IN (98,99,100) FOR UPDATE;

它超出错误锁定等待超时。

这里100已被锁定(在第一次交易中)但是ids 98,99没有被锁定。如果在上面的查询中只有100被锁定,那么有任何可能返回98,99的记录。所以结果应如下所示< / p>

编号

===

98

99

===

应忽略Id 100,因为100被事务锁定。

3 个答案:

答案 0 :(得分:5)

MySQL没有办法忽略SELECT中的锁定行。你必须找到一种不同的方法来将一行设置为“已经处理过”。

最简单的方法是在第一个查询中短暂锁定行只是为了将其标记为“已处理”,然后将其解锁并再次锁定以进行剩余的处理 - 第二个查询将等待“短”标记“查询完成,您可以添加显式WHERE条件以忽略已标记的行。如果您不想依赖第一个能够成功完成的操作,则可能需要在时间戳上添加更多复杂性,以便在这些操作失败后进行清理。

答案 1 :(得分:3)

在MySQL中,现在可以看到上一个答案中提到的NOWAIT选项。它不等待获取行锁,并允许您处理当前未锁定的行。

来自MySQL 8.0.0 Release Notes/Changes in MySQL 8.0.1:

  

InnoDB现在支持SKIP LOCKEDSELECT ... FOR SHARE选项,SELECT ... FOR UPDATENOWAIT锁定读取语句。如果请求的行被另一个事务锁定,SKIP LOCKED会导致语句立即返回。 START TRANSACTION; SELECT * FROM tableName FOR UPDATE SKIP LOCKED; 从结果集中删除锁定的行。 See Locking Read Concurrency with NOWAIT and SKIP LOCKED

样本用法(输出的完整示例可以在上面的链接中找到):

SKIP LOCKED

此外,也可以在此处参考手册中包含警告:

  

跳过锁定行的查询会返回数据的不一致视图。因此,{ "tokens": { "gift_name": "Cool Gift", "price": "$20" } } 不适合一般的交易工作。但是,当多个会话访问同一个类似队列的表时,它可用于避免锁争用。

答案 2 :(得分:0)

根据http://dev.mysql.com/doc/refman/5.0/en/innodb-locking-reads.html

解决方案是使用SELECT

以锁定模式执行LOCK IN SHARE MODE
SELECT * FROM parent WHERE NAME = 'Jones' LOCK IN SHARE MODE;