Java开发技巧——并发控制中的乐观锁与悲观锁
1、為什么需要鎖?
在多用戶環(huán)境中,在同一時間可能會有多個用戶新相同的記錄,這會產(chǎn)生沖突。這就是的并發(fā)性問題。
2、典型的沖突類型:
(1)丟失新:一個事務(wù)的新覆蓋了其它事務(wù)的新結(jié)果,就是所謂的新丟失。例如:用戶A把值從6改為2,用戶B把值從2改為6,則用戶A丟失了他的新。
(2)臟讀:當(dāng)一個事務(wù)讀取其它完成一半事務(wù)的記錄時,就會發(fā)生臟讀取。例如:用戶A,B看到的值都是6,用戶B把值改為2,用戶A讀到的值仍為6。
3、并發(fā)控制的機(jī)制
悲觀鎖:假定會發(fā)生并發(fā)沖突,屏蔽一切可能違反數(shù)據(jù)完整性的操作。
樂觀鎖:假設(shè)不會發(fā)生并發(fā)沖突,只在提交操作時檢查是否違反數(shù)據(jù)完整性。 樂觀鎖不能解決臟讀的問題。
4、樂觀鎖和悲觀鎖
悲觀鎖(Pessimistic Lock),就是很悲觀,每次去拿數(shù)據(jù)的時候都認(rèn)為別人會修改,所以每次在拿數(shù)據(jù)的時候都會上鎖,這樣別人想拿這個數(shù)據(jù)就會block直到它拿到鎖。傳統(tǒng)的關(guān)系型數(shù)據(jù)庫里邊就用到了很多這種鎖機(jī)制,比如行鎖,表鎖等,讀鎖,寫鎖等,都是在做操作之前先上鎖。
樂觀鎖(Optimistic Lock), 顧名思義,就是很樂觀,每次去拿數(shù)據(jù)的時候都認(rèn)為別人不會修改,所以不會上鎖,但是在新的時候會判斷一下在此期間別人有沒有去新這個數(shù)據(jù),可以使用版本號等機(jī)制。樂觀鎖適用于多讀的應(yīng)用類型,這樣可以提高吞吐量,像數(shù)據(jù)庫如果提供類似于write_condition機(jī)制的其實都是提供的樂觀鎖。
兩種鎖各有優(yōu)缺點(diǎn):
不可認(rèn)為一種好于另一種,像樂觀鎖適用于寫比較少的情況下,即沖突真的很少發(fā)生的時候,這樣可以省去了鎖的開銷,加大了系統(tǒng)的整個吞吐量。但如果經(jīng)常產(chǎn)生沖突,上層應(yīng)用會不斷的進(jìn)行retry,這樣反倒是降低了性能,所以這種情況下用悲觀鎖就比較合適。
5、樂觀鎖的應(yīng)用
1、使用自增長的整數(shù)表示數(shù)據(jù)版本號。新時檢查版本號是否一致,比如數(shù)據(jù)庫中數(shù)據(jù)版本為6,新提交時version=6+1,使用該version值(=7)與數(shù)據(jù)庫version+1(=7)作比較,如果相等,則可以新,如果不等則有可能其他程序已新該記錄,所以返回錯誤。
2、使用時間戳來實現(xiàn).
注:對于以上兩種方式,Hibernate自帶實現(xiàn)方式:在使用樂觀鎖的字段前加annotation: @Version, Hibernate在新時自動校驗該字段。
6、悲觀鎖的應(yīng)用
需要使用數(shù)據(jù)庫的鎖機(jī)制,比如SQL SERVER 的TABLOCKX(排它表鎖) 此選項被選中時,SQL Server 將在整個表上置排它鎖直至該命令或事務(wù)結(jié)束。這將防止其他進(jìn)程讀取或修改表中的數(shù)據(jù)。
在實際生產(chǎn)環(huán)境里邊,如果并發(fā)量不大且不允許臟讀,可以使用悲觀鎖解決并發(fā)問題;但如果系統(tǒng)的并發(fā)非常大的話,悲觀鎖定會帶來非常大的性能問題,所以我們就要選擇樂觀鎖定的方法.
悲觀鎖會造成訪問數(shù)據(jù)庫時間較長,并發(fā)性不好,特別是長事務(wù)。
樂觀鎖在現(xiàn)實中使用得較多,廠商較多采用。
一個典型的倚賴數(shù)據(jù)庫的悲觀鎖調(diào)用:
select * from account where name=”Erica” for update。
這條 sql 語句鎖定了account 表中所有符合檢索條件( name=”Erica” )的記錄。本次事務(wù)提交之前(事務(wù)提交時會釋放事務(wù)過程中的鎖),外界無法修改這些記錄。
Hibernate 的悲觀鎖,也是基于數(shù)據(jù)庫的鎖機(jī)制實現(xiàn)。
注意,只有在查詢開始之前(也就是Hiberate 生成 SQL 之前)設(shè)定加鎖,才會真正通過數(shù)據(jù)庫的鎖機(jī)制進(jìn)行加鎖處理,否則,數(shù)據(jù)已經(jīng)通過不包含 for update子句的 Select SQL 加載進(jìn)來,所謂數(shù)據(jù)庫加鎖也就無從談起。
樂觀鎖( Optimistic Locking )相對悲觀鎖而言,樂觀鎖機(jī)制采取了加寬松的加鎖機(jī)制。悲觀鎖大多數(shù)情況下依靠數(shù)據(jù)庫的鎖機(jī)制實現(xiàn),以操作大程度的獨(dú)占性。但隨之而來的就是數(shù)據(jù)庫性能的大量開銷,特別是對長事務(wù)而言,這樣的開銷往往無法承受。
7、經(jīng)典案例分析
如一個金融系統(tǒng),當(dāng)某個操作員讀取用戶的數(shù)據(jù),并在讀出的用戶數(shù)據(jù)的基礎(chǔ)上進(jìn)行修改時(如改用戶帳戶余額),如果采用悲觀鎖機(jī)制,也就意味著整個操作過程中(從操作員讀出數(shù)據(jù)、開始修改直至提交修改結(jié)果的全過程,甚至還包括操作員中途去煮咖啡的時間),數(shù)據(jù)庫記錄始終處于加鎖狀態(tài),可以想見,如果面對幾百上千個并發(fā),這樣的情況將導(dǎo)致怎樣的后果。
樂觀鎖機(jī)制在一定程度上解決了這個問題。
樂觀鎖,大多是基于數(shù)據(jù)版本( Version )記錄機(jī)制實現(xiàn)。何謂數(shù)據(jù)版本?即為數(shù)據(jù)增加一個版本標(biāo)識,在基于數(shù)據(jù)庫表的版本解決方案中,一般是通過為數(shù)據(jù)庫表增加一個 “version” 字段來實現(xiàn)。
讀取出數(shù)據(jù)時,將此版本號一同讀出,之后新時,對此版本號加一。此時,將提交數(shù)據(jù)的版本數(shù)據(jù)與數(shù)據(jù)庫表對應(yīng)記錄的當(dāng)前版本信息進(jìn)行比對,如果提交的數(shù)據(jù)版本號大于數(shù)據(jù)庫表當(dāng)前版本號,則予以新,否則認(rèn)為是過期數(shù)據(jù)。
對于上面修改用戶帳戶信息的例子而言,假設(shè)數(shù)據(jù)庫中帳戶信息表中有一個version 字段,當(dāng)前值為 1 ;
而當(dāng)前帳戶余額字段( balance )為 $100 。
1、操作員 A 此時將其讀出( version=1 ),并從其帳戶余額中扣除 50(100-$50 )。
2、在操作員 A 操作的過程中,操作員 B 也讀入此用戶信息( version=1 ),并從其帳戶余額中扣除 20(100-$20 )。
3、操作員 A 完成了修改工作,將數(shù)據(jù)版本號加一( version=2 ),連同帳戶扣除后余額( balance=$50 ),提交至數(shù)據(jù)庫新,此時由于提交數(shù)據(jù)版本大于數(shù)據(jù)庫記錄當(dāng)前版本,數(shù)據(jù)被新,數(shù)據(jù)庫記錄 version 新為 2 。
4、操作員 B 完成了操作,也將版本號加一( version=2 )試圖向數(shù)據(jù)庫提交數(shù)據(jù)( balance=$80 ),但此時比對數(shù)據(jù)庫記錄版本時發(fā)現(xiàn),操作員 B 提交的數(shù)據(jù)版本號為 2 ,數(shù)據(jù)庫記錄當(dāng)前版本也為 2 ,不滿足 “ 提交版本必須大于記錄當(dāng)前版本才能執(zhí)行新 “ 的樂觀鎖策略,因此,操作員 B 的提交被駁回。這樣,就避免了操作員 B 用基于 version=1 的舊數(shù)據(jù)修改的結(jié)果覆蓋操作員 A 的操作結(jié)果的可能。
8、總結(jié)
從上面的例子可以看出,樂觀鎖機(jī)制避免了長事務(wù)中的數(shù)據(jù)庫加鎖開銷(操作員 A和操作員 B 操作過程中,都沒有對數(shù)據(jù)庫數(shù)據(jù)加鎖),大大提升了大并發(fā)量下的系統(tǒng)整體性能表現(xiàn)。
需要注意的是,樂觀鎖機(jī)制往往基于系統(tǒng)中的數(shù)據(jù)存儲邏輯,因此也具備一定的局限性,如在上例中,由于樂觀鎖機(jī)制是在我們的系統(tǒng)中實現(xiàn),來自外部系統(tǒng)的用戶余額新操作不受我們系統(tǒng)的控制,因此可能會造成臟數(shù)據(jù)被新到數(shù)據(jù)庫中。在系統(tǒng)設(shè)計階段,我們應(yīng)該充分考慮到這些情況出現(xiàn)的可能性,并進(jìn)行相應(yīng)調(diào)整(如將樂觀鎖策略在數(shù)據(jù)庫存儲過程中實現(xiàn),對外只開放基于此存儲過程的數(shù)據(jù)新途徑,而不是將數(shù)據(jù)庫表直接對外公開)。
本文來自千鋒教育,轉(zhuǎn)載請注明出處。
總結(jié)
以上是生活随笔為你收集整理的Java开发技巧——并发控制中的乐观锁与悲观锁的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 10个常用的Python图像处理工具,建
- 下一篇: 大数据技术-hive窗口函数详解