11

更新:现在有SKIP LOCKEDMysqlNOWAIT和 Postgres。

下面是老问题。


我希望并发事务从表中选择一行,将其标记为“脏”,以便其他事务无法选择它,然后执行其余的事务。

我在使用select... for update此目的时遇到了麻烦,因为第二笔交易也有同样的竞争。请为不同的事务提供一个最小示例以选择不同的行

我的数据是:

mysql> select * from SolrCoresPreallocated;
+----+-------------+-----+-----+
| id | used_status | sid | cid |
+----+-------------+-----+-----+
|  1 |           0 |   0 | 400 |
|  2 |           0 |   0 | 401 |
|  3 |           0 |   0 | 402 |
|  4 |           0 |   0 | 403 |
|  5 |           0 |   0 | 404 |
|  6 |           0 |   0 | 405 |
+----+-------------+-----+-----+
6 rows in set (0.00 sec)

而且这些东西没有按预期工作:

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

mysql> select * from SolrCoresPreallocated order by id limit 1 for update;
+----+-------------+-----+-----+
| id | used_status | sid | cid |
+----+-------------+-----+-----+
|  1 |           0 |   0 | 400 |
+----+-------------+-----+-----+
1 row in set (0.00 sec)

...set the used_status to 1
...perform the rest of the operations

...作为第二笔交易

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

mysql> select * from SolrCoresPreallocated order by id limit 1 for update;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql>  rollback;
Query OK, 0 rows affected (0.00 sec)
4

1 回答 1

12

SELECT ... FOR UPDATE以独占模式锁定行,这意味着在第一个选择完成或回滚之前,第二个选择无法继续。这是因为第二次选择的结果可能会受到您锁定的行内容的影响,因此需要对该行进行读取锁定才能检查。

UNIQUE INDEX例如,如果您创建一个on id,您可以这样做;

select * from SolrCoresPreallocated where id=1 for update;

在第一笔交易中;

select * from SolrCoresPreallocated where id=2 for update;

在第二个中独立,因为唯一索引允许第二个选择找到正确的行,而无需读取锁定第一个。

编辑:要尽快获得“免费”行,唯一的方法就是进行两次交易;

  • BEGIN/SELECT FOR UPDATE/UPDATE to busy/COMMIT 以获取该行。
  • BEGIN/<process row>/UPDATE to free/COMMIT 处理该行并释放它。

这意味着您可能需要补偿操作,以防进程失败并回滚将更新行以释放的事务,但是由于 MySQL(或就此而言的标准 SQL)没有“获取下一个未锁定行”的概念“,你没有太多选择。

于 2012-10-06T10:16:21.793 回答