WebObjects/EOF/使用 EOF/樂觀鎖
請注意,本節介紹了 EOF 在資料庫層使用的鎖定策略,而不是 EOEditingContext 鎖定,後者在堆疊中處於更高的位置。有關 EOEditingContext 鎖定的資訊,請參見上下文和資料庫鎖定。
資料庫 API 可以使用兩種主要的鎖定方式:悲觀鎖定和樂觀鎖定。悲觀鎖定透過在第一個參與者開始進行更改時顯式鎖定對該行的訪問,從而主動阻止兩個參與者更新同一行(有效地鎖定其他人,直到更改完成)。樂觀鎖定允許參與者在沒有顯式鎖定的情況下對資源進行更改,假設成功,只有在更改確實導致衝突時才會失敗。EOF 在鎖定資料時採用樂觀方法。
舉個例子,來說明 EOF 中的樂觀鎖定是如何工作的,假設你有一個 Person EO,PersonID 為 123,名字為 "Bob",姓氏為 "Roberts"。如果你將名字更改為 "Robert" 並在你的編輯上下文中執行 saveChanges(),你會看到以下 SQL 傳送到你的資料庫
update Person set FirstName = 'Robert' where PersonID = 123 and FirstName = 'Bob' and LastName = 'Roberts'
樂觀鎖定的關鍵在於更新語句的 where 子句。如果你是唯一一個更改此 EO 的人,則此更新命令將返回更新了 1 行,這是 EOF 預期的成功結果。但是,假設另一個使用者在同一時間進行編輯並將 Bob 的姓氏更改為 'Wilson'。重新執行上面的查詢,你會發現沒有行被更新,因為限定符中的 LastName = 'Roberts' 子句不會匹配(資料庫中的姓氏現在是 'Wilson')。這種情況在 EOF 中的結果是從 saveChanges() 丟擲樂觀鎖定異常。
當你執行 EOModeler 時,你可能已經注意到屬性檢視中有一個帶有鎖定圖示的列。此列定義了你的實體的哪些屬性將用於樂觀鎖定。所有被選中屬性都將出現在你對物件進行更新的 'where' 子句中。請注意,所有鎖定列都會在更新時被檢查,而不僅僅是修改屬性的列。
人們在使用 EOF 時經常遇到一個問題,即使用 "不精確" 的型別作為鎖定列。我所指的不精確型別是指那些在 EO 的整個生命週期中可能發生精度變化的型別,即使它沒有被使用者修改。最臭名昭著的資料型別是日期和浮點型別。Java 對浮點型別的精度通常與資料庫的精度不匹配。結果是,當 EOF 從資料庫中取出浮點數,將其放入 Float 中,並將其放置在快照中時,僅僅是將其載入到 Float 中這一行為就會改變它的值。這通常會導致鎖定方面的非常奇怪的問題,即使沒有併發更新,你也最終會遇到樂觀鎖定失敗。這種情況下的建議是,如果你的資料庫出現問題,請取消選中具有日期和浮點型別的屬性的鎖定列。這樣做明顯的缺點是,這些欄位將不再有資格用於檢測樂觀鎖定失敗(即,該列的 "最後寫入獲勝",並且你的程式不會收到關於更新衝突的通知)。
mmalcolm 在 O'Reilly 2002 年 Mac OS X 大會上做了一個名為 "EOF 中缺乏衝突,或 '嘿,媽媽,有人覆蓋了我的資料!'" 的演講,並且幻燈片可以在http://conferences.oreillynet.com/presentations/macosx02/crawford_eoconflicts.pdf 上找到。
關於樂觀鎖定導致的資料庫伺服器負載增加(因為資料庫必須在更新之前驗證所有鎖定列的值)存在一些討論。
2006 年 1 月,在 WebObjects 5.3 上進行了一次基準測試,場景如下:
這在 2006 年 1 月的最新版本 FrontBase 上執行,使用的是 Intel iMac 上的 WO 5.3。
建立了兩個相同的實體,它們具有 3 個整型、3 個布林型、3 個字串和 3 個日期作為屬性,但其中一個鎖定所有屬性,另一個不鎖定任何屬性。在資料庫中插入了 10,000 個例項,然後對每個實體的同一個屬性進行更新,更新後的值相同。
對於鎖定所有列的實體,完成執行需要 25753 毫秒。對於沒有鎖定任何列的實體,完成執行需要 7111 毫秒。在這個例子中,使用這個資料庫和這個硬體,不鎖定任何列的速度快了 3.6 倍。
將場景修改為鎖定一個日期列,鎖定列的實體完成執行需要 25574 毫秒,而未鎖定列的實體完成執行需要 14125 毫秒,速度相差 1.81 倍。
這實際上取決於你的應用程式型別。如果你要進行 100,000 次更新,你可能關心多花了 190 秒(或者其他時間差)來執行更新。但是,如果你只是定期更新 "正常" 資料集,那麼多花幾毫秒可能無關緊要。顯然,權衡是資料完整性。
資料庫基準測試永遠不簡單,並且通常不適用於不同供應商的不同資料庫,因此請將這些結果謹慎看待。在進行大規模功能犧牲之前,請始終對你的應用程式進行效能分析。
這裡是最容易觸發它的方法。
- 將一個物件拉到 WO 中的編輯頁面。
- 現在通過後門使用命令列或其他獨立於 WebObjects 的工具直接編輯資料庫記錄中的一些屬性(這些屬性在 EOModel 中有鎖定圖示),這些屬性代表物件。
- 現在提交你的物件編輯頁面。
- 現在會丟擲一個樂觀鎖定異常,因為在你編輯物件之前從資料庫中讀取的原始快照與資料庫中現在的值不同。
假設我們有一個表,它有一個主鍵 'oid' 和一個樂觀鎖定的屬性,名為 'field1'。現在,我們獲取主鍵為 273 的物件,它的資料庫值為 "original"。
現在假設你在 WO 中的表單中將 field1 更改為 "new",並且在提交之前,假設你直接將資料庫中的 field1 值更改為 "changed",那麼當你提交時,EOF 生成的 SQL 類似於:UPDATE table T1 set field1 = "new" where oid = 273 and field1 = "original";
.... 此更新將失敗,因為沒有記錄具有 oid = 273 和 field1 = "original"。存在一個具有 oid = 273 和 field1 = "changed" 的記錄,但是 WO 不知道這一點,因為其他應用程式已經更改了它。
如果你在更改時記錄 SQL,你會看到樂觀鎖定會影響所有生成的 UPDATE 語句的 WHERE 部分。