为什么选择此选项进行更新示例工作


Why does this select for update example work?

我正在运行一些简单的脚本来测试我正在解决的完整性问题的可能解决方案。假设我有一张my_table

|foo     |
|1       |

我有两个片段:

// db_slow.php
<?php
$db = new PDO('mysql:host=localhost;dbname=my_playground;charset=utf8', 'root', '');
echo 'starting transaction<br />';
$db->beginTransaction();
$stmt = $db->query('select * from my_table for update');
$rows = $stmt->fetchAll();
echo 'count tables: ', count($rows), '<br />';
if (count($rows) == 1) {
    sleep(10);
    $db->query('insert into my_table(foo) VALUES(2)');
}
$db->commit();
echo 'done';
// db_fast.php
<?php
$db = new PDO('mysql:host=localhost;dbname=my_plyaground;charset=utf8', 'root', '');
echo 'starting transaction<br />';
$db->beginTransaction();
$stmt = $db->query('select * from my_table for update');
$rows = $stmt->fetchAll();
echo 'count tables: ', count($rows), '<br />';
if (count($rows) == 1) {
    $db->query('insert into my_table(foo) VALUES(3)');
}
$db->commit();
echo 'done';

CCD_ 2具有10秒的延迟以模拟比赛条件。

据我所知,select ... for update会锁定它选择的所有行。如果我运行db_slow,然后运行db_fastdb_fast也有10秒的延迟,因为正如我所期望的那样,它正在等待db_slow

然而,我没有得到的是输出:

// db_slow.php
starting transaction
count tables: 1
done
// db_fast.php
starting transaction
count tables: 2
done

my_table

|foo      |
|1        |
|2        |

据我所知,select ... for update会锁定为该事务选择的所有行。这就是我所期望的:

  1. db_slow:选择行1并锁定它
  2. db_slow:看到它只有1行,然后等待
  3. db_fast:尝试选择第1行,查看它是否已锁定,等待
  4. db_slow:插入具有"2"的行
  5. db_fast:继续,因为第1行已解锁
  6. db_fast:只选择了1行,所以它插入了"3"
  7. 最终获得foo: 1, 2, 3

上述输出和延迟似乎证实了步骤1、2、3、4。似乎db_fast在尝试获取锁定后正在运行select?我以为它会选择一行,然后锁定或等待。


一些相关问题:

当我用select ... lock in share mode运行这个时,我最终会使用

// db_slow.php
starting transaction
count tables: 1
done
// db_fast.php
starting transaction
count tables: 1
done

my_table

|foo      |
|1        |
|3        |

为什么db_slow即使认为表中只有1行(插入行的条件),也不插入行"2"?

我认为预期的行为有点偏离。在db_slow提交之前,表中的所有行都被锁定。在它提交之后,有两行。db_slow提交时,db_fast被取消阻止。因此,行为是:

  1. db_slow:选择行1并锁定它
  2. db_slow:看到它只有1行,然后等待
  3. db_fast:尝试选择第1行,查看它是否已锁定,等待
  4. db_slow:插入具有"2"的行
  5. db_slow:提交
  6. db_fast:取消阻止并读取2行
  7. db_fast:不执行任何操作
  8. 以foo:1,2结束