使用select ...进行更新以隔离行的最小示例

时间:2012-10-06 09:55:05

标签: mysql

更新:Mysql和Postgres现在有SKIP LOCKEDNOWAIT

老问题如下。


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

我在为此目的使用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)

1 个答案:

答案 0 :(得分:8)

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

如果您创建UNIQUE INDEX,例如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 /<进程行> / UPDATE以释放/ COMMIT处理该行并将其释放。

这意味着您可能需要在进程失败的情况下进行补偿操作并回滚将该行更新为空闲的事务,但由于MySQL(或标准SQL)没有“获取”的概念下一个解锁行“,你没有很多选择。