只选择未锁定的行mysql

Rex*_*Rex 8 mysql transactions

我通过以下查询在一个事务中锁定了一行

START TRANSACTION;

SELECT id FROM children WHERE id=100 FOR UPDATE;
Run Code Online (Sandbox Code Playgroud)

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

START TRANSACTION;

SELECT id FROM children WHERE id IN (98,99,100) FOR UPDATE;
Run Code Online (Sandbox Code Playgroud)

它会超出错误锁定等待超时.

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

ID

===

98

99

===

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

ake*_*den 11

看起来SKIP LOCKED以前的答案中提到的选项现在在 MySQL 中可用。它不会等待获取行锁,并允许您处理当前未锁定的行。

MySQL 8.0.0 发行说明/MySQL 8.0.1 中的更改开始:

InnoDB的现在支持NOWAIT,并SKIP LOCKED有选择SELECT ... FOR SHARESELECT ... FOR UPDATE锁定读语句。NOWAIT如果请求的行被另一个事务锁定,则使语句立即返回。SKIP LOCKED从结果集中删除锁定的行。请参阅使用NOWAIT和锁定读取并发性SKIP LOCKED

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

START TRANSACTION;
SELECT * FROM tableName FOR UPDATE SKIP LOCKED;
Run Code Online (Sandbox Code Playgroud)

此外,在此处的参考手册中包含警告可能也不错:

跳过锁定行的查询会返回不一致的数据视图。SKIP LOCKED因此不适合一般事务性工作。但是,当多个会话访问同一个类似队列的表时,它可以用来避免锁争用。


Bri*_*and 5

MySQL没有办法忽略SELECT中的锁定行.您必须找到一种不同的方法将行设置为"已处理".

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

  • @CMCDragonkai 是的,当考虑到各种故障模式时,这会变得非常复杂。为了解决您提到的特定问题,该行最初可以用时间戳标记为“正在处理”,然后另一个进程可能会出现并取消标记任何已标记为“正在处理”时间过长的行。 (4认同)
  • 一个问题是,在将其标记为“已处理”和解锁然后锁定以进行实际处理之间,该过程可能会失败,我们现在有一行标记为“已处理”,但实际上没有对其进行任何处理。 (2认同)