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

Jes*_*ose 10 mysql

更新:现在有SKIP LOCKEDNOWAITMysql和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)
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

......作为第二次交易

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)
Run Code Online (Sandbox Code Playgroud)

Joa*_*son 9

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

UNIQUE INDEX例如id,如果您创建一个on ,则可以执行;

select * from SolrCoresPreallocated where id=1 for update;
Run Code Online (Sandbox Code Playgroud)

在第一笔交易中;

select * from SolrCoresPreallocated where id=2 for update;
Run Code Online (Sandbox Code Playgroud)

在第二个独立的,因为唯一索引让第二个选择找到正确的行而不读取第一个.

编辑:要尽快获得"免费"行,唯一的方法是做两个交易;

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

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