Doctrine2 フレームワークを使用して、php + mysql プラットフォームで実行されているアプリケーションがあります。1 回の http リクエストで 3 つの db クエリを実行する必要があります: 最初の INSERT、2 番目の SELECT、3 番目の UPDATE。UPDATE は SELECT クエリの結果に依存します。同時 http 要求が発生する可能性が高くなります。このような状況が発生し、DB クエリが混在すると (例: INS1、INS2、SEL1、SEL2、UPD1、UPD2)、データの不整合が発生します。INS-SEL-UPD 操作の原子性を保証するにはどうすればよいですか? ある種のロックを使用する必要がありますか、それともトランザクションで十分ですか?
3 に答える
@YaK からの回答は、実際には良い回答です。一般的なロックの扱い方を知っておく必要があります。
具体的に Doctrine2 に対処すると、コードは次のようになります。
$em->getConnection()->beginTransaction();
try {
    $toUpdate = $em->find('Entity\WhichWillBeUpdated', $id,  \Doctrine\DBAL\LockMode::PESSIMISTIC_WRITE);
    // this will append FOR UPDATE http://docs.doctrine-project.org/en/2.0.x/reference/transactions-and-concurrency.html
    $em->persist($anInsertedOne);
    // you can flush here as well, to obtain the ID after insert if needed
    $toUpdate->changeValue('new value');
    $em->persist($toUpdate);
    $em->flush();
    $em->getConnection()->commit();
} catch (\Exception $e) {
    $em->getConnection()->rollback();
    throw $e;
}
更新のために取得する後続のすべての要求は、ロックを取得した 1 つのプロセスでこのトランザクションが終了するまで待機します。トランザクションが成功または失敗した後、Mysql は自動的にロックを解除します。デフォルトでは、innodb ロックのタイムアウトは 50 秒です。したがって、プロセスが 50 秒以内にトランザクションを終了しない場合、ロールバックしてロックを自動的に解放します。エンティティに追加のフィールドは必要ありません。
テーブル全体LOCKがすべての状況で機能することが保証されています。しかし、並行性に対処するのではなく、並行性を妨げるため、非常に悪い結果になります。ただし、スクリプトが非常に短い時間枠でロックを保持している場合は、許容できる解決策である可能性があります。
テーブルがInnoDBエンジンを使用している場合(MyISAMとのトランザクションはサポートされていません)、トランザクションは最も効率的なソリューションですが、最も複雑でもあります。
非常に具体的なニーズ(同じテーブルで、SELECTクエリの結果に応じて最初のINSERT、2番目のSELECT、3番目のUPDATE):
- トランザクションを開始します
 - レコードを挿入します。他のトランザクションは、独自のトランザクションがコミットされるまでこれらの新しい行を表示しません(非標準の分離レベルを使用しない限り)
 - SELECT ... LOCK INSHAREMODEを使用してレコードを選択します。これで、これらの行にREADロックが設定されました。他の人がこれらの行を変更することはできません。(*)
 - 何かを更新する必要があるかどうかを判断するために、計算する必要があるものをすべて計算します。
 - 必要に応じて行を更新します。
 - 専念
 - いつでもエラーが発生する可能性があります。デッドロックが検出された場合、MySQLはデッドロックを回避するためにトランザクションをロールバックすることを決定する場合があります。別のトランザクションが読み取ろうとしている行を更新している場合、トランザクションはしばらくの間ロックされているか、タイムアウトになっている可能性があります。
 
このように進めると、トランザクションの原子性が保証されます。
(*)一般に、このSELECTによって返されない行は、並行トランザクションに挿入される可能性があります。つまり、適切な予防措置を講じない限り、トランザクションの過程全体で存在しないことが保証されません。