2012-09-27 25 views
5

我目前正在玩弄交易,無法圍繞以下場景:爲什麼此事務在ActiveRecord中不起作用?

鑑於有用戶名「johnny」和全名「John Smith」。

我開始兩個軌道遊戲機和以該順序執行以下命令:

控制檯答:

ActiveRecord::Base.transaction { user = User.find_by_username("foo"); sleep 10; user.update_attribute(:full_name, "#{user.full_name}-1"); } 

控制檯B:

ActiveRecord::Base.transaction { user = User.find_by_username("foo"); sleep 10; user.update_attribute(:full_name, "#{user.full_name}-2"); } 

所以定時如下:

A讀「約翰史密斯」

B讀 「約翰·史密斯」

一個寫道: 「約翰·史密斯-1」

B進行讀寫 「約翰·史密斯-2」

根據我的數據庫類事務B應該不寫「約翰史密斯-2「,因爲數據讀取後發生了變化。所以交易應該回滾,交易A應該贏。我希望用戶名是「John Smith-1」,但結果是「John Smith-2」。

任何想法爲什麼發生這種情況或如何獲得預期的行爲?

親切的問候

尼爾斯

+0

它可能是連接的隔離級別。確保樂觀鎖定而不是遊標穩定性。而且,具有諷刺意味的是,這可能是因爲交易。在技​​術上,如果兩個塊都在事務中,則第二個應該*開始*直到第一個完成,或者它應該遵循在ACID中定義的規則,第二個事務*好像*第一個沒有發生,但是提交* *第二個直到第一個完成纔開始。 –

+0

是什麼讓你覺得這是activerecord特有的?如果從數據庫命令行界面執行類似的任務,結果是不同的? –

+0

@FrederickCheung取決於你使用的SQL。如果它是'SELECT ... FOR UPDATE',那麼它會有所不同。仍然不是OP所期望的。但是,這將是悲觀的鎖定:http://api.rubyonrails.org/classes/ActiveRecord/Locking/Pessimistic.html –

回答

4

據我瞭解交易是不是鎖定,交易的主要目的是爲了確保原子更改。例如,當您從支票賬戶中扣除資金並將其存入您的儲蓄賬戶時,您需要確保兩個INSERT都成功或者兩個都失敗,否則您的狀態將不一致。你需要的是鎖定,例如http://api.rubyonrails.org/classes/ActiveRecord/Locking/Optimistic.html

更新: ACID並不意味着回滾事務,據我所知。如果沒有錯誤,則交易將成功。你得到的結果取決於隔離。如果使用SERIALIZABLE級別,則會得到「John Smith-1-2」,但默認情況下,InnoDB使用REPEATABLE READ級別http://dev.mysql.com/doc/refman/5.0/en/set-transaction.html#isolevel_repeatable-read,這表示如果SELECT是非鎖定的(User.find_by...),它將不鎖定用於閱讀的記錄,並且會得到在事務A開始時創建的快照的原始結果(即,B到B的SELECT在A完成之前不會鎖定,如SERIALIZABLE的情況)。

UPDATE:同時,您可以檢查http://api.rubyonrails.org/classes/ActiveRecord/Locking/Pessimistic.html進行悲觀鎖定。

+0

謝謝你的答案首先。據我所知,MySQL的InnoDB-Transactions符合ACID(http://en.wikipedia.org/wiki/ACID)。您的解釋忽略了「I」,隔離意味着併發事務的結果與它們按順序執行的結果相同,實際上在這裏並非如此。所以問題是:爲什麼ActiveRecord不能這樣工作? – NilsHaldenwang

+0

啊,我明白了,thx的澄清。 – NilsHaldenwang

相關問題