您想要的是在事务上下文中进行SELECT ... FOR UPDATE。SELECT FOR UPDATE会将独占锁放在选定的行上,就像执行UPDATE一样。它也隐式地以READ COMMITTED隔离级别运行,而不管显式设置的隔离级别如何。请注意,SELECT ... FOR UPDATE对于并发非常不利,仅在绝对必要时才应使用。随着人们的剪切和粘贴,它也倾向于在代码库中成倍增加。
这是Sakila数据库中的一个示例会话,该会话演示了FOR UPDATE查询的某些行为。
首先,我们非常清楚,将事务隔离级别设置为REPEATABLE READ。通常这是不必要的,因为它是InnoDB的默认隔离级别:
session1> SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
session1> BEGIN;
session1> SELECT first_name, last_name FROM customer WHERE customer_id = 3;
+------------+-----------+
| first_name | last_name |
+------------+-----------+
| LINDA | WILLIAMS |
+------------+-----------+
1 row in set (0.00 sec)
在另一个会话中,更新此行。琳达结婚并更名:
session2> UPDATE customer SET last_name = 'BROWN' WHERE customer_id = 3;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
回到session1,因为我们处于REPEATABLE READ中,所以Linda仍然是LINDA WILLIAMS:
session1> SELECT first_name, last_name FROM customer WHERE customer_id = 3;
+------------+-----------+
| first_name | last_name |
+------------+-----------+
| LINDA | WILLIAMS |
+------------+-----------+
1 row in set (0.00 sec)
但是现在,我们希望对该行具有独占访问权限,因此我们在该行上调用FOR UPDATE。注意,现在我们获得了该行的最新版本,该版本在此事务之外的session2中进行了更新。那不是REPEATABLE READ,那是READ COMMITTED
session1> SELECT first_name, last_name FROM customer WHERE customer_id = 3 FOR UPDATE;
+------------+-----------+
| first_name | last_name |
+------------+-----------+
| LINDA | BROWN |
+------------+-----------+
1 row in set (0.00 sec)
让我们测试一下session1中设置的锁。请注意,session2无法更新该行。
session2> UPDATE customer SET last_name = 'SMITH' WHERE customer_id = 3;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
但是我们仍然可以从中选择
session2> SELECT c.customer_id, c.first_name, c.last_name, a.address_id, a.address FROM customer c JOIN address a USING (address_id) WHERE c.customer_id = 3;
+-------------+------------+-----------+------------+-------------------+
| customer_id | first_name | last_name | address_id | address |
+-------------+------------+-----------+------------+-------------------+
| 3 | LINDA | BROWN | 7 | 692 Joliet Street |
+-------------+------------+-----------+------------+-------------------+
1 row in set (0.00 sec)
而且我们仍然可以使用外键关系更新子表
session2> UPDATE address SET address = '5 Main Street' WHERE address_id = 7;
Query OK, 1 row affected (0.05 sec)
Rows matched: 1 Changed: 1 Warnings: 0
session1> COMMIT;
另一个副作用是,您大大增加了导致死锁的可能性。
在您的特定情况下,您可能需要:
BEGIN;
SELECT id FROM `items` WHERE `status`='pending' LIMIT 1 FOR UPDATE;
-- do some other stuff
UPDATE `items` SET `status`='working', `updated`=NOW() WHERE `id`=<selected id>;
COMMIT;
如果“做一些其他事情”不是必需的,而实际上您不需要保留有关行的信息,那么SELECT FOR UPDATE则是不必要且浪费的,您可以只运行更新:
UPDATE `items` SET `status`='working', `updated`=NOW() WHERE `status`='pending' LIMIT 1;
希望这有道理。
items
WHEREstatus
='pending'LIMIT 1 FOR UPDATE;” 进入两个线程时,似乎无法解决我的问题。并且他们都看到同一行,那么一个将锁定另一行。我希望不知何故,将能够绕过锁定的行和GOTO这是悬而未决..下一个项目