MySQL InnoDB "SELECT FOR UPDATE" - SKIP LOCKED 等效

MySQL InnoDB "SELECT FOR UPDATE" - SKIP LOCKED equivalent

当我们使用 InnoDB table 在 MySQL 中创建 "SELECT FOR UPDATE" 时,是否有任何方法可以跳过 "locked rows"?

例如:航站楼 t1

mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> select id from mytable ORDER BY id ASC limit 5 for update;
+-------+
| id    |
+-------+
|     1 |
|    15 |
| 30217 |
| 30218 |
| 30643 |
+-------+
5 rows in set (0.00 sec)

mysql> 

同时,终端t2:

mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> select id from mytable where id>30643 order by id asc limit 2 for update;
+-------+
| id    |
+-------+
| 30939 |
| 31211 |
+-------+
2 rows in set (0.01 sec)

mysql> select id from mytable order by id asc limit 5 for update;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> 

所以如果我启动一个查询强制它到 select 其他行,没问题。

但是有没有办法跳过锁定的行?

我猜这应该是并发过程中的冗余问题,但我没有找到任何解决方案。


编辑: 实际上,我的不同并发进程正在做一些看似非常简单的事情:

  1. 取第一行(不包含特定标志 - 例如:"WHERE myflag_inUse!=1")。

  2. 获得 "select for update" 的结果后,我更新标志并提交行。

所以我只想 select 尚未锁定的行 myflag_inUse!=1...


以下 link 可以帮助我理解超时的原因,但不能帮助我理解如何避免超时:

MySQL 'select for update' behaviour


mysql> SHOW VARIABLES LIKE "%version%";
+-------------------------+-------------------------+
| Variable_name           | Value                   |
+-------------------------+-------------------------+
| innodb_version          | 5.5.46                  |
| protocol_version        | 10                      |
| slave_type_conversions  |                         |
| version                 | 5.5.46-0ubuntu0.14.04.2 |
| version_comment         | (Ubuntu)                |
| version_compile_machine | x86_64                  |
| version_compile_os      | debian-linux-gnu        |
+-------------------------+-------------------------+
7 rows in set (0.00 sec)

抱歉,我认为您从错误的角度处理问题。如果您的用户想要列出 table 中满足特定选择条件的记录,那么您的查询应该 return 全部记录,或者 return 带有错误消息并且不提供任何结果集。但是查询不应该只返回结果的一个子集,导致用户相信他拥有所有匹配的记录。

应通过确保您的应用程序锁定尽可能少的行和尽可能短的时间来解决此问题。

不幸的是,到目前为止似乎没有办法跳过 select 中锁定的行进行更新。

如果我们可以使用像 Oracle 'FOR UPDATE SKIP LOCKED' 这样的东西就好了。

在我的例子中,并行启动的查询完全相同,并且在数百万行上包含一个 'where' 子句和一个 'group by'...因为查询需要介于运行 需要 20 秒和 40 秒,这是(我已经知道的)问题的主要部分。

我看到的唯一 -临时而非最佳- 解决方案 是移动一些(即:数百万)行,我不会(直接)使用以减少查询的时间

所以我仍然会有相同的行为,但我会等待更少的时间...

我期待一种方法来不 select select 中的锁定行。

我没有将此标记为答案,因此如果添加(或发现)来自 mysql 的新条款,我稍后可以接受...

使用一些 suitable LIMIT 分块浏览 PRIMARY KEY 中的 table,这样您就不会一次查看 "too many" 行.

通过使用 PK,您可以按谓词table 方式对事物进行排序;这实际上消除了死锁。

通过使用LIMIT,您将不会一次吃太多东西。 LIMIT应该体现为PK上的范围。这使得两个线程是否即将相互踩踏变得非常清楚。

更多详细信息(间接地)在我的 blog on big deletes.

这似乎现在存在于 MySQL 从 8.0.1 开始:

https://mysqlserverteam.com/mysql-8-0-1-using-skip-locked-and-nowait-to-handle-hot-rows/

Starting with MySQL 8.0.1 we are introducing the SKIP LOCKED modifier which can be used to non-deterministically read rows from a table while skipping over the rows which are locked. This can be used by our booking system to skip orders which are pending. For example:

但是,我认为该版本不一定已准备好生产。

MySQL 8.0 引入了对 SKIP LOCKEDNO WAIT.

的支持

SKIP LOCKED 对于实现作业队列(a.k.a 批处理队列)很有用,这样您就可以跳过已被并发事务锁定的锁。

NO WAIT 有助于避免等待并发事务释放我们也有兴趣锁定的锁。

如果没有NO WAIT,我们要么必须等到锁被释放(在当前持有锁的事务提交或释放时),要么锁获取超时。 NO WAIT 充当锁定超时值 0.

有关 SKIP LOCKNO WAIT 的更多详细信息。