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