並行性の問題については、「簡単」があります。 2番目の方法で同時実行の問題を防ぐ方法として、トランザクション内で記事の行で選択を実行します(For update
現在は暗黙的です)。同じ記事への同時挿入は、この同じロックを取得できず、あなたを待ちます。
新しいデフォルトの分離レベルでは、トランザクションでシリアル化レベルを使用しなくても、トランザクションが終了するまで投票テーブルに同時挿入は表示されません。したがって、SUMは一貫性を保つ必要がありますまたは一貫性があるように見えます 。ただし、同時トランザクションが同じ記事に投票を挿入し、あなたの前にコミットした場合(そして、この2番目のトランザクションはあなたの挿入を認識しません)、コミットする最後のトランザクションはカウンターを上書きし、1票を失います。 そのため、前に選択を使用して記事の行ロックを実行します (もちろん、トランザクションで作業を行います)。テスト、MySQLで2つのインタラクティブセッションを開き、BEGINでトランザクションを開始するのは簡単です。
トリガーを使用する場合、デフォルトでトランザクションになります。ただし、同時トリガーを実行するための暗黙的な行ロックを作成するには、記事テーブルでselectも実行する必要があると思います(テストが困難です)。
- 削除トリガーを忘れないでください。
- 更新トリガーを忘れないでください。
- トリガーとステイインコードを使用しない場合は、投票に対するすべての挿入/削除/更新クエリが、トランザクションの前に対応する記事に対して行ロックを実行する必要があることに注意してください。忘れるのはそれほど難しいことではありません。
最後のポイント:トランザクションの使用を開始する前に、より難しいトランザクションを作成します:
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
このように、記事の行ロックは必要ありません。MySQLは、同じ行への潜在的な書き込みが発生したことを検出し、完了するまで他のトランザクションをブロックします。 ただし、以前のリクエストから計算したものは使用しないでください 。最初のトランザクションCOMMIT
によってロックが解放されると、更新クエリは記事のロック解放を待機します。 SUM
の計算 カウントするには、もう一度実行する必要があります。したがって、更新クエリにはSUM
が含まれている必要があります または追加します。
update articles set nb_votes=(SELECT count(*) from vote) where id=2;
そして、ここでは、MySQLがスマートであり、挿入が同時に行われている間に2つのトランザクションがこれを行おうとすると、デッドロックが検出されることがわかります。シリアル化レベルでは、:
を使用して間違った値を取得する方法を見つけていません。 SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN;
insert into vote (...
update articles set nb_votes=(
SELECT count(*) from vote where article_id=xx
) where id=XX;
COMMIT;
ただし、やり直さなければならない中断トランザクションを処理する準備をしてください。