以下假设在MySQL 5.6上以InnoDB表的事务中执行所有语句。同时,col1和col2共同形成唯一键。
如果我在Session 1中执行
现在假设
这种行为符合我的预期。然而,如果我们这样做:
现在,我真正想要的是,对于相同(尚不存在的)行上的第二个
如果我在Session 1中执行
SELECT * FROM table WHERE col1 = 1 AND col2 = 1 FOR UPDATE
,我将获取该行的数据并锁定该行。因此,如果我在Session 2中执行相同的语句,它将等待释放该锁或超时之前不会执行任何操作。到目前为止,一切顺利。现在假设
SELECT ... FOR UPDATE
返回一个空集,并且我们尝试在另一个会话中插入一些内容:-- Session 1
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT * FROM `table` WHERE `col1` = 1 AND `col2` = 1 FOR UPDATE;
Empty set (0.01 sec)
-- Do whatever else takes some time
-- Session 2
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO `table` SET `col1` = 1, `col2` = 1;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
这种行为符合我的预期。然而,如果我们这样做:
-- Session 1
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT * FROM `table` WHERE `col1` = 1 AND `col2` = 1 FOR UPDATE;
Empty set (0.00 sec)
-- Session 2
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT * FROM `table` WHERE `col1` = 1 AND `col2` = 1 FOR UPDATE;
Empty set (0.00 sec)
mysql> INSERT INTO `table` SET `col1` = 1, `col2` = 1;
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
现在,我真正想要的是,对于相同(尚不存在的)行上的第二个
SELECT ... FOR UPDATE
也能像INSERT INTO
一样等待。如何在不锁定整个表的情况下实现这一点,因为其他行需要保持可访问性。
select for update
检查特定列是否已经存在,如果不存在,则创建并保存该列。然而,会话2看不到即将出现的行,因为处理发生在MySQL外的php脚本中。在这一行实际存在之前,可能需要几毫秒的时间。我当然可以绕过这个问题,但按照我上面所说的方式做将是一个更干净的解决方案,我个人认为。 - Anpan