在了解Hibernate的LockMode之前,我們先講一下LockMode是什么東西?其實LockMode只是在使用Hibernate 中 的session.load()加載數據時指定的模式,也叫悲觀鎖(模式),然而,悲觀鎖是為了彌補read-committed 機制的不足,從而解決non-repeatable (不可重復讀)和 phantom-read (幻讀)問題 ,而non-repeatable 和 phantom-read 這兩個問題也只是事務并發是產生的兩種問題...
看了我寫的這一段后,我相信很多讀者會有點懵,這就對了,看完下面的文章,再后過頭來讀這一段,就全都明白了。
一、事務的特性
我們知道,事務由那幾個特性,四個(ACID):
1.原子性(Atomicity):
整個事務中的所有操作,要么全部完成,要么全部不完成,不可能停滯在中間某個環節。事務在執行過程中發生錯誤,會被回滾(Rollback)到事務開始前的狀態,就像這個事務從來沒有執行過一樣。
2.一致性(Consistency)
在事務開始之前和事務結束以后,數據庫的完整性約束沒有被破壞。
3.隔離性(Isolation)
兩個事務的執行是互不干擾的,一個事務不可能看到其他事務運行時,中間某一時刻的數據。
4.持久性(Durability)
在事務完成以后,該事務所對數據庫所作的更改便持久的保存在數據庫之中,并不會被回滾。
由于一項操作通常會包含許多子操作,而這些子操作可能會因為硬件的損壞或其他因素產生問題,要正確實現ACID并不容易。ACID建議數據庫將所有需要更新以及修改的資料一次操作完畢,但實際上并不可行。
一個支持事務(Transaction)的數據庫系統,必需要具有這四種特性,否則在事務過程(Transaction processing)當中無法保證數據的正確性,交易過程極可能達不到交易方的要求。
在處理事務過程中,事務并發是不可避免的,從而會出現以下幾個問題:
1.丟失更新(Lost Update)(第一類和第二類)
2.臟讀(Dirty Read)
3.不可重復讀(Non-repeatable Read)
4.幻讀(Phantom Read)
二、事務隔離機制
針對并發事務出現的問題,Hibernate 采用了數據庫的事務隔離機制(詳細文檔見Herbernate 參考文檔的 java.sql.Connection ),一般有以下四種處理機制:
1) read-uncommitted
2) read-committed
3) repeatable read
4) serializable
2.1 四種機制的具體價值:
A. 只要數據庫支持事務,就不可能出現第一類丟失更新 。
B. read-uncommitted(允許讀取未提交的數據)會出現dirty read , phantom-read,non-repeatable read 問題。
C. read-committed(讀取已提交的數據 ,項目中一般使用這個,MySql 數據庫默認是這種機制)不會出現dirty read ,因為只有另一個事務提交才會讀出來結果,但仍然會出現non-repeatable和phantom-read(所以使用read-committed 機制可用悲觀鎖和樂觀鎖來解決non-repeatable 和 phantom-read 問題),在hibernate中的事務隔離級別的設定(使用 hibernate.connection.isolation配置,取之1,2,4,8)。
1.hibernate.connection.isolation = 2 (如果不設置,默認依賴數據庫本身的級別,例如MySql為2,read-committed)。
2.使用悲觀鎖解決repeatable read 的問題(依賴于數據庫的鎖) 。
a. 使用悲觀鎖查找數據或者更新數據時,默認加上for update ,表示對當前事務加鎖,其他事務暫時不能使用,例如 select ... for update
b. 在程序中如何加上悲觀鎖呢,很簡單,直接在session的load方法中添加一個參數即可,例如:
Account a = (Account)session.load(Account.class, 1, LockMode.UPGRADE);
鎖的模式:
a.LockMode.None 無鎖的機制,Transaction 介紹時,切換到此模式
b.LockMode.read 在查詢的時候hibernate會自動獲取鎖
c.LockMode.write insert update hibernate會自動獲取鎖
d.以上3種鎖的模式,是hibernate內部使用的(不需要設置)
e.LockMode.UPGRADE_NOWAIT 是Oracle 支持的索的模式
3.使用樂觀鎖解決repeatable read 的問題 。
直接在實體類中添加 version 屬性(數據庫也會對應生成該字段,初始值為0),并在其get 方法前加 @version 注解 (這里介紹注解方式配置),則在操作過程中每更新一次改行數據則在 version 值上加1,即可在事務提交前判斷該數據是否被其他事務修改過。
D. repeatable read (事務執行中其他事務無法執行修改或插入操作 較安全)。
E. serializable 解決事務隔離級別(順序執行事務,不并發,實際當中很少使用)。
三、悲觀鎖
在應用程序中顯示地為數據資源加鎖.悲觀鎖假定當前事務操縱數據資源時,肯定還會有其它事務同時訪問該數據資源,為了避免當前事務的操作受到干擾,先鎖定資源.盡管悲觀鎖能防止丟失更新和不可重復讀這類并發問題,但會影響并發性能.(簡單理解,就是每次在操作數據時總是悲觀地認為會有別的事務也會來操縱同一數據,從此鎖住該筆數據,直到自己操作完成后再解除鎖)。
四、樂觀鎖
假定當前事務操縱數據資源時,不會有其它事務同時訪問該數據資源,因此完全依靠數據庫的隔離級別來自動管理鎖的工作.應用程序采用版本控制手段來避免可能出現的并發問題.(所謂樂觀鎖,它通常認為多個事務同時操縱同一數據的情況是很少的,因為根本不做數據庫層次上的鎖定,只是基于數據的版本標識實現應用程序級別上的鎖定機制,既保證了多個事務的并發訪問,又有效地防止了第二類丟失更新的出現)。
LockMode類表示的幾種鎖定模式
鎖定模式描述
LockMode.NONE 如果緩存中存在對象,直接返回該對象的引用,否則通過select語句到數據庫中加載該對象,默認值.
LockMode.READ 不管緩存中是否存在對象,總是通過select語句到數據庫中加載該對象,如果映射文件中設置了版本元素,就執行版本檢查,比較緩存中的對象是否和數據庫中對象版本一致
LockMode.UPGRADE 不管緩存中是否存在對象,總是通過select語句到數據庫中加載該對象,如果映射文件中設置了版本元素,就執行版本檢查,比較緩存中的對象是否和數據庫中對象的版本一致,如果數據庫系統支持悲觀鎖(如Oracle/MySQL),就執行select...for update語句,如果不支持(如Sybase),執行普通select語句
LockMode.UPGRADE_NOWAIT 和LockMode.UPGRADE具有同樣功能,此外,對于Oracle等支持update nowait的數據庫,執行select...for update nowait語句,nowait表明如果執行該select語句的事務不能立即獲得悲觀鎖,那么不會等待其它事務釋放鎖,而是立刻拋出鎖定異常
LockMode.WRITE 保存對象時會自動使用這種鎖定模式,僅供Hibernate內部使用,應用程序中不應該使用它
LockMode.FORCE 強制更新數據庫中對象的版本屬性,從而表明當前事務已經更新了這個對象
多個事務并發運行時的并發問題
第一類丟失更新:撤銷一個事務時,把其它事務已提交的更新數據覆蓋。
第二類丟失更新:不可重復讀中的特例,一個事務覆蓋另一事務已提交的更新數據。
臟讀:一個事務讀到另一事務未提交的更新數據。
幻讀:一個事務讀到另一事務已提交的新插入的數據。
不可重復讀:一個事務讀到另一個事物已提交的更新數據。
五、鎖的類型和兼容性
共享鎖
加鎖條件:當一個事務執行select語句時,數據庫系統會為這個事務分配一把共享鎖,鎖定被查詢的數據。
解鎖條件:數據被讀取后,數據庫系統立即解除共享鎖。
與其它鎖的兼容性:如果數據資源上放置了共享鎖,還能再放置共享鎖和更新鎖。
并發性能:良好的并發性能.當多個事務讀相同數據時,每個事務都會獲得一把共享鎖,可以同時讀鎖定的數據。
獨占鎖
加鎖條件:當一個事務執行insert,update,delete時,數據庫系統會自動對SQL語句操縱的數據資源使用獨占鎖.如果該數據資源已經有其它鎖存在時,無法對其再放置獨占鎖。
解鎖條件:獨占鎖一直到事務結束后才能被解除。
與其它鎖的兼容性:獨占鎖不能和其他鎖兼容,如果數據資源已經加上了獨占鎖, 就不能再放置其它鎖,同樣,如果已經有了其它鎖,就不能放置獨占鎖。
并發性能:并發性能較差,只允許有一個事務訪問鎖定的數據,如果其他事務也需要訪問該數據,就必須等待,直到前一個事務結束,解除了獨占鎖,其它事務才能訪問該數據。
更新鎖
加鎖條件:當一個事務進行update操作時,數據庫系統會先為事務分配一把更新鎖.
解鎖條件:當讀取數據完畢,執行更新操作時,會把更新鎖升級為獨占鎖。
與其它鎖的兼容性:更新鎖與共享鎖兼容,即一個資源可以同時放置更新鎖和共享鎖,但是最多只能放置一把更新鎖,這樣,當多個事務更新相同的數據時,只有一個事務能獲得更新鎖,然后再把更新鎖升級為獨占鎖,其它事務必須等到前一個事務結束后,才能獲得更新鎖,避免了死鎖。
并發性能:允許多個事務同時讀鎖定資源,但不允許其它事務修改它。
各種隔離級別所能避免的并發問題