0

mysql pdo クエリを実行する php スクリプトがあります。このスクリプトでは、同じテーブルに対していくつかの読み取りと書き込みがあります。

例として、読み取り、書き込み、別の読み取り、別の書き込みの 4 つのクエリがあり、各読み取りの実行に 10 秒かかり、各書き込みの実行に 0.1 秒かかるとします。

CLI からこのスクリプトをnohup php execute_queries.php &1/100 秒で 2 回実行すると、クエリの実行順序はどうなりますか?

スクリプトの最初のインスタンスからのすべてのクエリは、2 番目のインスタンスからのクエリの実行が開始される前に終了する必要がありますか?それとも、テーブルが書き込みによってロックされる前に、両方のインスタンスからの最初の読み取りが開始および終了しますか?

注: myisam を使用していて、書き込みがレコードの更新であると仮定します (つまり、書き込み中にテーブル全体がロックされます)。

4

1 に答える 1

1

トランザクションを使用していないので、いいえ、1 つのスクリプト内のすべてのクエリが終了するのを待たないため、クエリが重複する可能性があります。

これを教える並行プログラミングと呼ばれる研究分野全体があります。

データベースでは、トランザクション、分離レベル、およびデータ ロックが重要です。

典型的な (単純な) 競合状態:

$visits = $pdo->query('SELECT visits FROM articles WHERE id = 44')->fetch()[0]['visits'];
/*
 * do some time-consuming thing here
 *
 */
$visits++;
$pdo->exec('UPDATE articles SET visits = '.$visits.' WHERE id = 44');

上記の競合状態は、2 つの PHP プロセスが1 ミリ秒後にデータベースから訪問を読み取ると、簡単に悪化する可能性があります。望ましい効果は、2 回の訪問で値が 2 増加することでした (訪問の最終的な値は8 になるはずでした)。

これに対する解決策は、アトミック操作を使用することです (操作が単純で、1 つのアトミック操作に減らすことができるため)。

UPDATE articles SET visits = visits+1 WHERE id = 44;

アトミック操作は、データベース エンジンによって、他のプロセス/スレッドによって中断されずに実行されることが保証されています。通常、データベースは着信更新をキューに入れ、相互に影響を与えないようにする必要があります。各プロセスは、実行される機会が得られるまで、その前にすべてのプロセスを待機する必要があるため、キューイングは明らかに速度を低下させます。

単純ではない操作では、複数のステートメントが必要です。

SELECT @visits := visits FROM articles WHERE ID = 44;
SET @visits = @visits+1;
UPDATE articles SET visits = @visits WHERE ID = 44;

しかし、データベース レベル 3 でも、個別のアトミック ステートメントがアトミックな結果を生成する保証はありません。それらは他の操作と重複する可能性があります。PHPの例と同じです。

これを解決するには、次のことを行う必要があります。

START TRANSACTION
    SELECT @visits := visits FROM articles WHERE ID = 44 FOR UPDATE;
    SET @visits = @visits+1;
    UPDATE articles SET visits = @visits WHERE ID = 44;
COMMIT;
于 2012-05-28T22:00:26.980 に答える