解決しようとしている整合性の問題の可能な解決策をテストするために、いくつかの簡単なスクリプトを実行しています。テーブルがあるとしますmy_table
|foo |
|1 |
そして、私はこれらの2つのスニペットを持っています:
// 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';
db_slow.php
競合状態をシミュレートするために 10 秒の遅延があります。
私が理解しているように、select ... for update
選択したすべての行をロックします。db_slow
thendb_fast
を実行すると、期待どおりにdb_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
に、そのトランザクションで選択されているすべての行をロックします。だから、これは私が期待するものです:
- db_slow: 行 1 を選択してロックする
- db_slow: 1行しかないことを確認して待つ
- db_fast: 行 1 を選択してみてください。ロックされていることを確認してください。
- db_slow: '2' で行を挿入
- db_fast: 行 1 がロック解除されているため続行します
- db_fast: 1 行のみを選択したため、「3」を挿入します
- で終わる
foo: 1, 2, 3
上記の出力と遅延は、ステップ 1、2、3、4 を確認しているようdb_fast
です。ロックを取得しようとした後に select を実行しているように見えますか? 1行を選択してから、ロックまたは待機すると思いました。
やや関連する質問:
これを実行すると、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' を挿入しないのはなぜですか(行を挿入する条件)?