此文章測試專案,使用spring3 + hibernate4 與 Mysql 來作測試
1.面臨問題
報名系統的開課人數10人,
假設以報名9人,剩下名額1人
同時間有數人一起報名,
如何避免競賽情況(Race Condition)!?
預期解決方案:
1.隔離層
2.樂觀鎖定
3.悲觀鎖定
4.Synchronized
2. 交易管理的概念
交易(Transaction)是一個單元工作(unit of work),這個單元工作包括了數個步驟來完成,這數個步驟必須全部執行成功,交易才算成功,只有當中有一個失敗,則整個交易宣告失敗。
A.原子性(Atomicity)
一個交易是一個單元工作,當中可能包括數個步驟,這些步驟正常全部執行成功,若有一個失敗,則整個交易宣告失敗,交易中其它步驟也必須撤消曾經執行過的動作,回到交易前的狀態。
B.一致性(Consistency)
指交易所作用的資料集合在交易前後必須一致,也就是這組資料集合若交易成功,則整個資料集合都必須是交易操作後的狀態,若交易失敗,所整個資料集合必須與開始交易之前一樣完全沒有變更,不能發生整個資料集合,部份有變更,而部份沒變更的狀態。
C.隔離行為(Isolation behavior)
在多人使用的環境下,每個使用者可能進行自己的交易,交易與交易之間,必須互不干擾,使用者不會意識到別的使用者正在進行交易,就好像只有自己在進行操作一樣。
D.持續性(Durability)
交易一旦成功,則所有的變更必須保存下來,即使系統掛了,交易的結果也不能遺失。
3.Spring 的 交易管理
主要可以設定4個參數
A.傳播行為
B.隔離層級
C.唯獨提示
D.交易超過時間
§Propagation.REQUIRED
支援現在的交易,如果沒有 就建立一個新的交易。
支援現在的交易,如果沒有 就建立一個新的交易。
§Propagation.REQUIRES_NEW
不管是否存在交易,都創建一個新的交易,原來的暫停,新的執行完畢,繼續執行原來的交易。(常用於記錄LOG) 。
不管是否存在交易,都創建一個新的交易,原來的暫停,新的執行完畢,繼續執行原來的交易。(常用於記錄LOG) 。
§Propagation.SUPPORTS
調用的類別方法若有使用交易,就加入交易,若沒有就不使用交易。
調用的類別方法若有使用交易,就加入交易,若沒有就不使用交易。
§Propagation.NOT_SUPPORTED
容器不為這個方法開啟交易。(若再加入交易會丟出例外)
容器不為這個方法開啟交易。(若再加入交易會丟出例外)
§Propagation.MANDATORY
方法必須在一個現存的交易中進行,否則丟出例外。
方法必須在一個現存的交易中進行,否則丟出例外。
§Propagation.NEVER
指不應在交易中進行,如果有的話就丟出例外。
指不應在交易中進行,如果有的話就丟出例外。
§Propagation.NESTED
在一個巢狀的交易中進行,如果不是的話,則同Propagation.REQUIRED 。
在一個巢狀的交易中進行,如果不是的話,則同Propagation.REQUIRED 。
§Isolation = Isolation.DEFAULT
使用底層資料庫預設的隔離層級。
§Isolation=Isolation.READ_UNCOMMITTED
允許交易讀取其它並行交易還沒送出的資料,會發生Dirty read 、
允許交易讀取其它並行交易還沒送出的資料,會發生Dirty read 、
Non
Repeatable 、 phantom
read、
lost update等問題。
§Isolation = Isolation.READ_COMMITTED
允許交易讀取其它並行交易已送出的資料欄位,可防止 Dirty read 、。
允許交易讀取其它並行交易已送出的資料欄位,可防止 Dirty read 、。
§Isolation = Isolation.REPEATABLE_READ
要求多次讀取的資料必須相同,除非交易本身更新資料,會發生 phantom read問題。
要求多次讀取的資料必須相同,除非交易本身更新資料,會發生 phantom read問題。
§Isolation = Isolation.SERIALIZABLE
完整的隔離層級,可以防止Dirty read 、 Non Repeatable 、 phantom read等問題,但是會鎖定資料表格,因而有效能問題。
完整的隔離層級,可以防止Dirty read 、 Non Repeatable 、 phantom read等問題,但是會鎖定資料表格,因而有效能問題。
§隔離交易的基本方式是鎖定資料庫,但完全的鎖定資料庫實務上並不會這麼作,因為將導致嚴重的效能問題,因此實務上會根據資料讀寫更新的頻繁性,設定不同的交易隔離層級。
§MYSQL: 預設为REPEATABLE_READ
§髒讀(dirty read)
兩個交易同時進行,其中一個交易更新資料,另一個交易讀取了
未COMMIT的資料,就有可能發生髒讀問題。例如:
1.交易A 更新欄位1
2.交易B 讀取欄位1
3.交易A ROLLBACK
4.交易B COMMIT
在以上的情況下,交易B讀取的是不正確的資料
§不一致(Non Repeatable)
某個交易兩次讀取同一欄位的資料並不一致,例如,如果交易A
在交易B前後進行資料的讀取,則會得到不同的結果。
1.交易A讀取欄位1
2.交易B更新欄位1
3.交易B COMMIT
4.交易A讀取欄位1
在以上的情況,交易A讀取兩次欄位1,但卻得到不同的結果。
§幻讀(phantom read)
如果交易A進行兩次查詢,在兩次查詢之中有個交易B插入一筆新
資料或刪除一筆新資料,第二次查詢時得到的資料多了第一次
查詢時所沒有的筆數,或者少了一筆。
1.交易A進行查詢得到五筆資料
2.交易B插入一筆資料
3.交易B COMMIT
4.交易A進行查詢得到六筆資料
例如:
用戶A讀取學號為107的學生(學號=107,姓名=“小明”,年齡=
28)
用戶B讀取學號為107的學生(學號=107,姓名=“小明”,年齡=
28)
用戶A把姓名更改(學號=107,姓名=“王小明”,年齡= 28)
用戶B把年齡更改為33(學號=107,姓名=“小明”,年齡=33)
用戶A提交(學號=107,姓名=“王小明”,年齡=
28)
用戶B提交(學號=107,姓名=“小明”,年齡=
33)
用戶A對學生姓名的更新丟失了。
單獨使用隔離層會產生Deadlock錯誤,可搭配樂觀鎖、悲觀鎖、Synchronized ,來防止丟失更新錯誤。
§readOnly = false
§如果交易只進行讀取的動作,則可以利用底層資料庫在唯讀操
作時的一些最佳化動作,由於這個動作利用到資料庫在唯讀的交易操作最佳化,因而必須在交易中才有效,也就是要搭配下列傳播行為 :
A.PROPAGATION_REQUIRED
B.PROPAGATION_REQUIRES_NEW
C.PROPAGATION_NESTED
§timeout=30
§有的交易操作可能延續一段很長的時間,交易本身可能關聯到資料表格的鎖定,因而長時間的交易操作會有效能上的問題,對於過長的交易操作,要考慮回滾(Roll back)交易並要求重新操作,而不是無限時的等待交易完成,必須搭配下列傳播行為
:
A.PROPAGATION_REQUIRED
B.PROPAGATION_REQUIRES_NEW
C.PROPAGATION_NESTED
10.解決方案1: 設定 Isolation
@Transactional(
readOnly = false,
propagation = Propagation.REQUIRED,
isolation=Isolation.DEFAULT
)
§樂觀鎖定(Optimistic
locking)樂觀的認為資料很少發生同時存取的問題,通常在資料庫層級上設為read-commited隔離層級,並實行樂觀鎖定。
§樂觀鎖定Hibernate所推薦的方式,在資料庫中加入一個version欄位記錄,在讀取資料時
連同版本號一同讀取,並在更新資料時比對版本號與資料庫中的版本號,如果等於資料庫中的版本號則予以更新,並遞增版本號,如果小於資料庫中的版本號就丟出 StaleObjectStateException(JPA)例外。
§根據
底層 ORM
不同,會丟出不同例外
@Version
@Column(name = "VERSION")
private Integer version;
Get (略)
Set (略)
§悲觀鎖定(Pessimistic
Locking)一如其名稱所示,悲觀的認定每次資料存取時,其它的客戶端也會存取同一筆資料,因此對相關的資料進行鎖定,直到自己操作完成後解除鎖定。
簡略介紹
§NONE
不使用鎖定。
§OPTIMISTIC_FORCE_INCREMENT
當使用版本號進行 樂 觀鎖定 (Optimistic Locking 時,可強迫指定的物件進行版本號遞增)。
§PESSIMISTIC_WRITE
交易將立即啟用數據庫鎖定。
§WRITE
在insert或update時進行鎖定,Hibernate會在進行資料寫入時自動獲得鎖定。
§如果資料庫不支援所指定的鎖定模式,Hibernate會選擇一個合適的鎖定替換,而不是丟出一個例外。
17.解決方案4: Synchronized
§根據不同情況,可以使用不同的解決方法,以下提供
幾種方案
1.Transaction 搭配 樂觀鎖定
優點:適用於資料發生衝突機會低的情況,可以搭配錯誤訊息: 系統忙碌中,請稍後再用 。 缺點:多個JVM Cluster下無法使用,DB 資料表 多系統讀寫無法使用。
2.Transaction 搭配 悲觀鎖定
優點:適用於資料發生衝突機會高的情況,和資料需要正確性。
缺點:犧牲資料庫速度,根據不同資料庫鎖定方式也不同分成row lock(消耗資源高) 、table lock (消耗資源低) ,(MySQL 採用 row lock)。
3.Transaction 搭配 Synchronized
優點:從程式面多執行緒來作處理,強制Lock物件,來確保交易正確性。 缺點:犧牲整體程式速度,且在多個JVM Cluster下無法使用。
投影片下載:
參考資料:







沒有留言:
張貼留言