2011-03-24 76 views
5

在我的Rails應用程序中,我有一些像這樣的代碼:如何鎖定一組操作對象?

def foo 
    if object_bar_exists 
    raise "can't create bar twice!" 
    end 

    Bar.create 
end 

這可以通過進入應用服務器的兩個不同的請求來調用。如果此代碼由兩個請求同時運行,並且它們都同時運行if檢查,則既不會發現另一個的bar,也不會創建2個bar

爲「酒吧的集合」創建「互斥」的最佳方式是什麼?數據庫中的特殊目的互斥表?

更新

我要強調的是,我不能在這裏使用一個內存互斥的,因爲併發是跨請求/過程,而不是線程。

回答

4

最好的辦法是在數據庫事務中執行操作。因爲您可能最終會運行多個應用程序,並且它們很可能不會共享內存,所以您將無法在應用程序級別創建Mutex鎖定,特別是如果這兩個應用程序服務在完全不同的物理盒子上運行。下面是如何完成數據庫事務:

ActiveRecord::Base.transaction do 
    # Transaction code goes here. 
end 

如果你想確保數據庫事務回滾,那麼你就必須有在酒吧類啓用驗證,這樣一個無效的保存請求都將導致回滾:

ActiveRecord::Base.transaction do 
    bar = Bar.new(params[:bar]) 
    bar.save! 
end 

如果你已經有了一個數據庫中的酒吧對象,你可以悲觀地鎖定該物體像這樣:

ActiveRecord::Base.transaction do 
    bar = Bar.find(1, :lock => true) 
    # perform operations on bar 
end 
+1

我認爲事務只能確保它們內部的一切都是自動執行的。不能同時使用兩個進程運行相同的事務代碼,並且有相同的問題? (最初的'if'檢查並行運行並且成功,然後寫入並行發生,並且在兩種情況下所有相應的代碼都以原子方式發生) – 2011-03-24 20:03:53

+0

如果鎖定該行,則不會。這是數據庫中的行級鎖定,因此另一個事務無法訪問它。此外,如果您有確保唯一行的驗證錯誤,則第一次寫入將成功,下一次將失敗並回滾整個事務。 – 2011-03-24 22:16:09

+2

您確實希望事務**和**數據庫中的唯一索引來強制解決問題。當事情出錯時,交易將保持一切清潔,唯一的索引會在事情出錯時告訴你。 – 2011-03-24 22:18:51

1

如果他們所有的請求都進入同一臺機器和同一個ruby虛擬機,那麼可以使用Ruby內置的Mutex類:Mutex Docs

如果有多臺機器或rvms,則必須使用數據庫事務來創建/獲取Bar對象,假設它以某種方式存儲在數據庫中。

0

我可能會create a Singleton對象在lib目錄中,以便您只有一個事件的實例,並使用Mutexes來鎖定它。

這樣,您可以確保在任何給定的時間點只有一次訪問該事物。當然,任何其他請求都會阻止它,所以請記住。

對於多臺機器,您必須將令牌存儲在數據庫中,並同步對令牌的某種訪問。比如,令牌必須被查詢並取出,並跟蹤某個號碼或某物以確保人們不能同時移除令牌。或者使用集中鎖定的Web服務,以便您的令牌處理僅在一個位置。