2

残念ながら、MyISAMテーブルでレガシーmysql_ *関数を使用するアプリケーションがあるため(bleagh ...)、トランザクションを使用できません。現在の残高を取得し、この残高に問題がないかどうかを確認するコードがあります。問題がない場合は、数量を差し引いて新しい残高を保存します。

問題は、最近、2つのクエリが同じ開始残高を取得し、数量を減算してから、新しい残高を記録する例を見たことです。両方とも同じ開始バランスを取得したため、両方のUPDATES後の終了バランスが間違っています。

100 - 10 = 90
100 - 15 = 85

いつあるべきか...

100 - 10 = 90
90 - 15 = 75

これらのリクエストは数分間隔で実行されたため、競合状態によるものではないと思います。私の最初は、MySQLキャッシュが、バランスをとる同一の初期クエリの結果を保存しているということです。ただし、関連するテーブルが変更されると、このタイプのキャッシュは削除されることを読みました。

ほとんどの場合、すべてを1つのクエリにまとめることで修正しますが、それでもこれを理解したいと思います。それは私を神秘的にします。テーブルが変更されたときにキャッシュが削除された場合、起こったことは起こってはならないはずです。誰かがこのようなことを聞​​いたことがありますか、またはなぜそれが起こったのかについて何か考えがありますか?

4

3 に答える 3

2

クエリキャッシュである可能性は非常に低いです。MySQLは、基になるデータセットが別のクエリによって変更された場合にキャッシュエントリを無効にするほど賢いです。クエリキャッシュが期限切れを過ぎても古い古い値を保持している場合、MySQLはまったく役に立たないでしょう。

これを引き起こしている未解決のコミットされていないトランザクションがある可能性がありますか?関連するレコードに適切なロックがないと、2番目のクエリで古いデータを非常に簡単に取得する可能性があります。

于 2012-05-01T21:31:07.700 に答える
2

アプリケーションに古いデータが含まれている可能性があります。これは問題ありません。動作するデータベース アプリケーションの数ですが、更新を実行するときは、次のようにする代わりに、次のようにします。

UPDATE account
SET balance = :current_balance - 10
WHERE account_id = 1

次のようなことをする必要があります。

UPDATE account
SET balance = balance - 10
WHERE account_id = 1

そうすれば、誰かが途中で変更したとしても、古いアプリケーション データに頼るのではなく、データベースの現在の残高を使用できます。

誰も変更していない場合にのみ値を変更したい場合は、次のようにします。

UPDATE account
SET balance = balance - 10
WHERE account_id = 1
  AND balance = :current_balance

影響を受けた行の数が 1 の場合、成功し、レコードは他の誰かによって変更されていません。ただし、影響を受けた行の数が 0 の場合は、他の誰かがレコードを変更したことになります。そこから何をしたいかを決めることができます。

于 2012-05-01T22:16:58.470 に答える
1

テーブルをロックすることはあなたの問題の解決策だと思います:)

于 2012-05-01T21:33:03.503 に答える