3

MySQLとPHP+Propel 1.3を使用しているときに、同時実行の問題が発生しているようです。以下は、Propelオブジェクトの「保存」メソッドの小さな例です。

public function save(PropelPDO $con = null) {
    $con = Propel::getConnection();
    try {
        $con->beginTransaction();
        sleep(3); // ignore this, used for testing only
        parent::save($con);
        $foo = $this->getFoo(); // Propel object, triggers a SELECT

        // stuff is happening here...

        $foo->save($con);
        $con->commit();
    } catch (Exception $e) {
        $con->rollBack();
        throw $e;
    }
}

問題は$fooオブジェクトです。非常に短い時間で、サンプルメソッドの2つの呼び出しを次々に取得するとします。場合によっては、2番目のトランザクションが$foo...を読み取る場合

$foo = $this->getFoo();

...最初のトランザクションがそれを保存する機会を得る前に...

$foo->save($con);

...2番目のトランザクションによって読み取られた$fooは古くなり、悪いことが起こります。

Fooオブジェクトが格納されているテーブルを強制的にロックして、最初のトランザクションが作業を終了した後にのみ後続のトランザクションがそこから読み取ることができるようにするにはどうすればよいですか?

編集:コンテキストはWebアプリケーションです。要するに、場合によっては、最初のリクエストでデータの変更を行いたいことがあります(これは、$ fooのフェッチと保存の間に発生します)。以降のすべてのリクエストで変更を行うことはできません。変更が発生するかどうかは、フェッチされた$ fooの状態(テーブルの行属性)によって異なります。2つのトランザクションが同じ$fooをフェッチする場合、変更が2回発生し、問題が発生します。

4

2 に答える 2

1

この既存の行を画面/アプリケーションにロードするときは、LastChgDateもロードします。保存するときは、「AND LastChgDate=thevalue」を使用してください。更新の影響を受ける行数を確認し、ゼロの場合は、「他の誰かがすでにこのレコードを保存しています」というエラーを返し、ロールバックおよびその他の変更を行います。このロジックを配置すると、ロードしたときと同じ場合にのみ行を保存できます。新しい行のINSERTの場合、これらは新しいため、これは必要ありません。

于 2009-06-01T18:31:44.753 に答える
0

MySQLでは、SELECTFORUPDATEを使用してロックを実行できると思います。

もう1つのオプションは、GET_LOCKおよびRELEASE_LOCK MySQL関数呼び出しを使用して、リソースへのアクセスを制御するために使用する名前付きロックを作成することです。

これらのアプローチにはいくつかの欠点があります。私自身はあまり使用していません。MySQL固有ですが、機能する可能性があります。

于 2009-06-01T18:39:34.473 に答える