2012-05-01 8 views
1

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

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

100 - 10 = 90 
100 - 15 = 85 

それがあるべき は...

100 - 10 = 90 
90 - 15 = 75 

は、これらの要求は離れて数分を実行するので、私は不一致が競合状態が原因であるとは考えていません。私の最初は、MySQLキャッシュは、バランスを取る同じ初期クエリの結果を格納しているということです。しかし、関連するテーブルが変更された場合、このタイプのキャッシュは削除されます。

すべてを1つのクエリに入れて修正する可能性がありますが、これを把握したいと考えています。それは私を神秘的にする。テーブルが変更されたときにキャッシュが削除された場合、起こったことは起こってはいけません。誰かがこのようなことを聞​​いたことがありますか、それが起こったかもしれない理由についてのアイデアがありますか?

+1

この現象を再現するコードの簡略化されたバージョンを投稿できますか? –

+0

mysql_ *でトランザクションを使用することはできませんか? innoDBテーブルを使用している場合は、mysql_query( "START TRANSACTION")などを実行するだけです。 – bfavaretto

+0

@bfavaretto残念ながら、彼らはMyISAMです。 – dqhendricks

答えて

1

テーブルをロックすることはあなたの問題に対する解決策だと私は考えます:

2

クエリキャッシュとなる可能性は低い - MySQLは、基礎となるデータセットが別のクエリによって変更された場合にキャッシュエントリを無効にするほどスマートです。クエリキャッシュが古い失効値を期限切れにしていた場合、MySQLはまったく役に立たないでしょう。

おそらく未解決のコミットされていないトランザクションがこれを引き起こしていますか?関連するレコードが適切にロックされていないと、2番目のクエリで失効したデータを簡単に取得できます。

+0

私は同意します。それはキャッシングの問題ではありません。アプリケーションに古くなったデータがある可能性が最も高いです。OPはそれがMYISAMだと言ったので、取引はありません。 –

+0

データを読み書きする前にすべてのテーブルをロックするようにしました。データの読み込みにキャッシュも指定しません。問題は再び発生しました。ここで何が起こっているのか想像できません。マーカスが述べたように、それはMYISAMなので、取引はありません。アプリケーションの他の領域からすべての編集を無効にしました。私は関連する変数のコードを検索し、予期しない割り当てはありません。 299/300回このコードは正常に動作し、月に1回はこの問題が発生します。 – dqhendricks

2

ほとんどの場合、アプリケーションに古いデータがあります。

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

あなたはより多くのこのような何かを実行する必要があります:これは、多くのデータベース・アプリケーションがどのように動作するかですが、あなたの更新を実行する場合、代わりにこのような何かをすることの、大丈夫です

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

その方法失効したアプリケーションデータに頼るのではなく、たとえ誰かがそれを変更したとしても、データベースからの現在の残高を使用します。

あなたは誰もがそれを変更しなかった場合にのみ値を変更したい場合は、あなたがこのような何か:影響を受けた行数が1である場合、あなたは、記録HADNに成功し、

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

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

+0

私は先にバランスをチェックして新しいエントリを作成し、残高を更新する必要があるという点を除いて、これを行います。私はテーブルロックを実装するつもりです。 – dqhendricks

+0

@dqhendricks、この同時ロックを実装する必要があります。同時実行性は問題ではないからです。あなたは、リクエストが数分離れていたと言いました。 –

+0

さて、最初のSELECTでSQL_NO_CACHEをロックしていたテーブルを実行しました。これは私が望んでいる古いキャッシュデータの問題を解決するはずです。あなたの解決策には、できるだけ避けたいリファクタリングが必要です。しかし、私はそれをもう一度見たら...私は戻ってきます。 – dqhendricks

関連する問題