2011-12-30 7 views
3

私はRails 3.1とMySQL 5.1を使ってオークションのようなオークションを設計しています。ユーザーには勘定残高があるため、資金が不足しているとオークションアイテムに入札しないことが重要です。オークション/銀行のようなアプリケーション(Rails/MySQL)の楽観的または悲観的なロック

トランザクション1:

もちろん、私は、トランザクションにオークションの「勝利」梱包されますが、このようなものになりところで

ActiveRecord::Base.transaction do 
    a = Account.where(:id=>session[:user_id]).first 
    # now comes a long part of code with various calculations and other table updates, i.e. time pases 
    a.balance -= the_price_of_the_item 
    a.save! 
end 

を、私はcurerntlyしたがって、楽観的ロックを使用しています私のテーブルにはすべてlock_versionという列があります。そのようなトランザクションが実行されている間、彼らは入札、コードチェックのピースを置くたびに

は、ユーザが別の入力場所を介して他の入札、従って現在利用可能な残高が再びここで

同じで十分であることができれば:

トランザクション2:

ActiveRecord::Base.transaction do 
    a = Account.where(:id=>session[:user_id]).first 
    raise ActiveRecord::Rollback if a.balance < the_price_of_the_bid + Bids.get_total_bid_value_for_user(session[:user_id]) 
    # now process the bid saving 
end 

はもちろん、私は、トランザクション1が処理中であると私はウィットに終わる一方、そうでない場合は、トランザクション2がバランスを読んだことがあり、2つのトランザクションが重複しないことを、確認する必要がありますh負の口座残高(入札が保存された後、取引1がコミットした後、ユーザーはおそらく自分が持っていない資金で入札する可能性があります)。

注意すべき点は、トランザクション2はアカウントを変更せず、アカウントを読み取るだけであることです。トランザクション1の実行中に選択されたSELECT文の読み込みを防ぐ方法を教えてください。

トランザクション1を完了するためにトランザクション2を待機させるにはどうすればよいですか?オプティミスティックロックと利用可能なMySQLトランザクション分離レベルのいずれかが可能か、ここで悲観的ロックを使用する必要がありますか?暗示的なロックが唯一の答えである場合は、 a.lockを追加します。 2つの取引のそれぞれの口座レコードを読んだ後に で十分でしょうか?私はそれがより コーディングを意味していても、最もパフォーマンスの高いソリューションを探しています

  • デザインのcriteriasはもちろんです。

  • データの整合性が最も重要である今の支出を持っ
+0

あなたのデータを理解しようとしています。誰かが10の残高を持っているとします。彼らは10の2つの入札を作成することができます(この時点で、いずれかまたはいずれかが勝つかどうかわかりません)。彼らができない場合、これをどのように追跡していますか? –

+0

いいえ、私は彼らが両方の入札を獲得し、そのアカウントの残高が-10になるリスクがあります。トラッキングは取引2で行われ、使用可能な残高があるかどうかを確認し、ユーザーの口座に十分な金額があれば、入札が承認され、保存されます。 – KKK

+0

このコードでは、現在開いている入札があるかどうかもチェックされます。 (または勘定残高に反映されますか?) –

答えて

2

、ほぼ10時間ノンストップ、私は私の調査結果集計する様々な記事や文書だけでなく、裁判を読んだとRailsコンソールを使用してerroring:

オプティミスティックロック:私の要件に対処するためには役に立たない、ロックはアカウント残高レコードを実際に保存する場合にのみ有効です。しかし、入札を置いても口座レコードは更新されないので、すべての未確定入札に対して現在のコミット済み資金を追跡している入札記録のフィールドを維持しないと、入札時に口座レコードを更新します入札が保存されるたびに別のDBの更新が必要になるため、やりたいことはありません)。

このようにすれば、私は悲観的なロックのみを残します。

トランザクション1:

ActiveRecord::Base.transaction do  
    u = User.find(session[:user_id],:lock=>true) 
    a = Account.where(:id=>session[:user_id]).first 
    a.balance -= the_price_of_the_item 
    ... some more code here ... 
    a.save!  
end  

とトランザクション2:

ActiveRecord::Base.transaction do 
    u = User.find(session[:user_id],:lock=>true) 
    raise ActiveRecord::Rollback if a.balance < the_price_of_the_bid + Bids.get_total_bid_value_for_user(session[:user_id])   
    # now process the bid saving 
    .... 
end 

単純化のために、私はこのように私のトランザクション1のコードに変更され、ユーザーレコードのロックを置くことにしましたさらに、私はMySQLトランザクション分離レベルをSERIALIZABLEに設定することに決めました。

1

私はあなたの質問を次のように解釈します。アンアカウント残高

  • アンアカウントA入札を価値もないウォンを持ち、また失わ
  • A入札、ビッグ "開く"
  • あるがあり、多くの入札
  • を持っていた

    1. その当座預金の価値は、口座の残高から控除されます。
    2. 「入札」の入札単価の合計が残高よりも小さい場合にのみ、入札を行うことができます。

    このように、重要な取引を特定しました。これは私がそれを行うだろうかある入札

  • 勝ち入札

  • を置く

    1. account = Account.find_by_id(session[:user_id]) 
      # maybe do some stuff here 
      
      transaction do 
          account.lock! 
      
          bid_amount = account.bids.open.sum(:value) 
          if bid_amount + this_value > account.balance 
          raise "you're broke, mate" 
          end 
      
          account.bid.create!(:value => this_value) 
      end 
      

      1.

      私たちはしばらくの間、 のアカウント上の行ロックを保持していますが、右のデータベースを使用していると仮定すると、これは大丈夫でなければなりません。あなたは第二のロックを行う必要がありませんSET balance = balance - ? SQLの更新を行った場合は、最初のビットは正しかったと仮定すると、

      は、その後、次は

      2.

      class Bid 
          def win! 
          transaction do 
           account.lock! 
           account.decrement(:balance, self.value) 
           account.save! 
           close! 
          end 
          end 
      end 
      

      注目すべきははるかに簡単です1。

      しかし、一般的に、コードの最小限の部分についてはロックを行います。

      現実的には、100ミリ秒以上はロックしないでください。

  • +0

    他のプロセスがアカウント残高を更新する場合は、 アカウントの残高を読み取る account = Account.find_by_id(session [:user_id] ) および トランザクションdo account.lock! then bid_amount + this_value> account.balance が間違っている場合。 つまり、パート2がPArt 1.の検索とロックの間で実行されている場合、パート1は間違ったアカウント残高を使用しています。ではない? – KKK

    +0

    私は 'ロック! 'はリロードを行い、その時点で残高をリロードすると信じています。 –

    +0

    ロック!絶対にリロードする:https://github.com/rails/rails/blob/master/activerecord/lib/active_record/locking/pessimistic.rb – mrm

    関連する問題