7

テーブル全体 (単一の行ではなく) をドクトリンでロックする必要があります。可能であれば、ネイティブ クエリを使用せずにこれを実行したいと考えています。

ペシミスティック ロックのドキュメントでは、次の方法で特定のエンティティをロックする方法についてのみ説明しています。

  • EntityManager#find
  • EntityManager#ロック
  • クエリ#setLockMode

テーブル内の残りの行の値に依存する値を持つ行を挿入する必要があるトランザクションがあるため、そのテーブルで 2 つのトランザクションが同時に実行されるのを防ぐ必要があります。

私は、明示的なトランザクション境界を使用しています。これは、ロックでうまく機能するはずです (上記のドキュメントによると)。

: この場合、楽観的ロックは十分ではありません。トランザクションを再試行する余裕はありません。さらに、クエリが遅くなることは想定されていないため、パフォーマンスは問題になりません。

編集:例を挙げます。auto_increment を手作業で作成したいと想像してください。次の結果を挿入するには、前の結果を取得するためにテーブルから max() を選択する必要があります。max() を同時に選択した場合に備えて、2 つのトランザクションが同じ値を挿入しようとしないようにする必要があります。

たとえば、文字列、複数の列、ハッシュ、または前の行セットで行う必要がある計算など、 auto_increment が適切でない場合、この問題の一般的な解決策を探しています。

ロックは確実な解決策であり、楽観的ロックとは異なり、エラーが発生した場合に再試行する必要はありません。

では、ドクトリンでテーブルロックを使用する方法はありますか?

4

3 に答える 3

4

これまでのアドバイスに従って、私はこれを試しました:

$em->getConnection()->exec('LOCK TABLES table_name WRITE;'); //lock for write access

// calculate $new_number...

// persist $new_number on table_name...
$table_name->setCalculatedNumber($new_number);
$em->persist($table_name);
$em->flush();

$em->getConnection()->exec('UNLOCK TABLES;');

JMeter でテストしましたが、負荷が高い (16 リクエスト/秒) とロックが機能しませんでした。トレースは、他のインスタンスが明示的に放棄される前にロックを取得したことを示しました。問題 (Jens が示唆) は、flush() がテーブル ロックを削除する START TRANSACTION で暗黙的に開始されることでした。ネイティブ アップデートを使用すると、問題が解決しました。

$em->getConnection()->exec('LOCK TABLES table_name WRITE;'); //lock for write access

// calculate $new_number...

// persist $new_number on table_name...
$em->getConnection()->executeUpdate("UPDATE table_name set ...;");    

$em->getConnection()->exec('UNLOCK TABLES;');
$em->refresh($table_name);

後続のクエリ結果で計算された数値を使用できるようにするために、末尾の refresh() が必要でした。

于 2015-04-08T19:06:21.870 に答える
2

Doctrine2 ORM select for updateによって重複している可能性があります

関連するコードを次に示します。

try {
    $entity = $em->find('User', $theEntityId, LockMode::OPTIMISTIC, $expectedVersion);

    // do the work

    $em->flush();
} catch(OptimisticLockException $e) { 
    echo "Sorry, but someone else has already changed this entity. Please apply the changes again!";
}

LockMode::OPTIMISTIC パラメーターは、必要なものを提供する場合があります。

于 2013-12-11T21:14:24.480 に答える
1

Doctrine 2.x のドキュメントを見ると、テーブル全体をロックするサポートされている方法はないと思います。もちろん、Doctrine を介してすべてのレコードを個別にロックすることもできますが、これは面倒であり、あまり良い考えではありません。

代わりに、Doctrine エンティティ マネージャーを使用して、データベース上で生の SQL を実行します...

$em->getConnection()->exec('LOCK TABLES table_name WRITE;'); //lock for write access

そして、更新が完了したら...

$em->getConnection()->exec('UNLOCK TABLES;');

編集:

テーブルロックに関するMySQLのドキュメントから...

  • ロックを保持しているセッションは、テーブルを読み書きできます。

  • ロックを保持しているセッションのみがテーブルにアクセスできます。ロックが解除されるまで、他のセッションはアクセスできません。

  • WRITE ロックが保持されている間、他のセッションによるテーブルのロック要求はブロックされます。

ここでの 2 番目のポイントが重要だと思います。そのテーブルに対して読み取り/書き込みができるのは、セッションだけです。

于 2012-07-03T12:09:30.510 に答える