mysqlによる勘定残高の更新 | serialization,mysql

ユーザーのアカウント残高を保持するフィールドがUserテーブルにあります。ユーザーは自分のサービスで多くのアクションを実行することができ、その結果、バランスが急速に変化します。

私は、複数のユーザアクションが値を間違って更新しないように、mysqlの直列化可能な分離レベルを使用しようとしています。
(アクションAとアクションBは同時に天びんから1ドルを差し引きたいと思っています。)しかし、デッドロック・エラーが多く発生しています。

これらのデッドロックをすべて取得せずに残高フィールドを最新の状態に保つことなく、これを正しく行うにはどうすればよいですか?

単純なスキーマ:ユーザーはIDと残高を持ちます。

私はdoctrineを使っているので、私は次のようなことをしています:

$con->beginTransaction();
$tx = $con->transaction;
$tx->setIsolation('SERIALIZABLE');

$user = UserTable::getInstance()->find($userId);
$user->setBalance($user->getBalance() + $change);
$user->save();
$con->commit();
ベストアンサー

まず、シリアライズ可能な分離レベルをトランザクションに使用しようとするとよいでしょう。これは、少なくとも変換が何であるかを知ることを意味し、分離レベルは最大の問題の1つです。

シリアライズ可能であることは実際には本当のセリビリティではないことに注意してください。
こちらの前回の回答
をご覧ください。それを読む時間があります:-)。

しかし、最も重要な点は、シリアライゼーションが失敗したためにトランザクションに自動ロールバックがあることは通常の事実であることを考慮する必要があり、トランザクションが失敗して再生する必要があるようにアプリケーションを構築することです。

1つの簡単なソリューションと会計処理のために私はすべての事実を予測できるので、この単純な解決策が好きです。驚きはありませんので、テーブルロックを実行することです。これは上級で洗練されたソリューションではなく、行レベルのロックもなく、シンプルな大きなテーブルロック(常に同じ順序)です。その後、あなたは1人のプレイヤーとして操作を行い、次にロックを解放することができます。テーブルの行に複数のユーザ並行性があるわけではなく、次の行のマジカルロックは失敗しません(前のリンクを参照)。これにより書き込み操作は確実に遅くなりますが、誰でも同じ順序でテーブルロックを実行すると、ロックタイムアウトの問題、デッドロックの発生、および「シリアル化不可能な自動ロールバック」が発生しません。

編集

あなたのコードサンプルから、トランザクションの分離レベルを開始後に設定することはできません。
MySQLでクエリログを有効にして、完了したら、CMSによって実行されている他のトランザクションがまだシリアル化可能レベルにないことを確認する必要があります。

コメントする

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です