2

解決しようとしている整合性の問題の可能な解決策をテストするために、いくつかの簡単なスクリプトを実行しています。テーブルがあるとします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_slowthendb_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に、そのトランザクションで選択されているすべての行をロックします。だから、これは私が期待するものです:

  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 を実行しているように見えますか? 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' を挿入しないのはなぜですか(行を挿入する条件)?

4

1 に答える 1

1

期待される動作は少しずれていると思います。db_slow がコミットする前に、テーブル内のすべての行がロックされます。コミット後、2 つの行があります。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 で終わる
于 2013-07-23T17:36:58.593 に答える