您好,登錄后才能下訂單哦!
本篇內容主要講解“JPA怎么使用樂觀鎖應對高并發方式”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“JPA怎么使用樂觀鎖應對高并發方式”吧!
JPA使用樂觀鎖應對高并發
高并發系統的挑戰
悲觀鎖的問題
樂觀鎖是個好東西
給數據庫添加樂觀鎖
樂觀鎖 -業務判斷 解決高并發
在部署分布式系統時,我們通常把多個微服務部署在內網集群中,再用API網關聚合起來對外提供。為了做負載均衡,通常會對每個微服務都啟動多個運行實例,通過注冊中心去調用。
那么問題來了,因為有多個實例運行都是同一個應用,雖然微服務網關會把每一個請求只轉發給一個實例,但當面對高并發時,但它們仍然可能同時操作同一個數據庫表,這會不會引發什么問題呢?
比如電商中常見的商品秒殺系統,在用戶搶購商品過程中,會有大量并發請求,很可能同時讀寫一個包含商品剩余數量的表,這種一般要給數據庫加鎖,否則很容易出現商品超賣錯賣的情況。
如果使用數據庫自帶的鎖機制,也就是悲觀鎖,在寫入的時候鎖定數據庫,其他修改請求到來時就必須等待鎖釋放,但這就使效率降下來了,而且高并發場景下可能有的請求一直搶不到鎖,就會長時間卡在那導致請求失敗,然后有大量重試,系統可能會發生連接數耗盡等異常。
查閱資料發現樂觀鎖是個好東西,它是為數據庫表增加一個標識數據版本的version字段來實現的,讀取數據時把version字段一同讀出,寫入數據庫時比對version字段就知道數據是否被更改過,如果version不相等就說明持有的是過期數據,不能寫入,如果相等就可以寫入,并把version加一。
樂觀鎖在寫入數據庫的時候,才會檢查數據是否沖突,如果發現沖突了,就放棄寫入,返回寫入失敗的信息,相比于悲觀鎖,這是一種輕量級的對數據的鎖定方式,能夠應對高并發需求。
說樂觀鎖是個好東西,首先得說 JPA 是個好東西,因為Spring Data JPA已經內置了樂觀鎖的實現,給數據庫表添加樂觀鎖很簡單,添加一個整型字段,并加入@Version注解就可以了,每次提交數據時JPA會自動檢查版本。
@Entity @Table(name = "m_order") public class Order { ... @Version private int version; ... }
在解決高并發問題時,如果是分布式系統顯然我們只能夠使用數據庫端加鎖機制來解決這個問題,但是這種同步機制或者數據庫物理鎖機制會犧牲一部分的性能,所以常常以另外一種方式來解決這個問題 就是樂觀鎖模式
銀行兩操作員同時操作同一賬戶就是典型的樂觀鎖模式。
比如A、B操作員同時讀取一余額為1000元的賬戶,A操作員為該賬戶增加100元,B操作員同時為該賬戶扣除50元,A先提交,B后提交。最后實際賬戶余額為1000-50=950元,但本該為1000+100-50=1050。這就是典型的并發問題。
樂觀鎖機制在一定程度上解決了這個問題。樂觀鎖,大多是基于數據版本(Version)記錄機制實現。何謂數據版本?即為數據增加一個版本標識,在基于數據庫表的版本解決方案中,一般是通過為數據庫表增加一個 “version” 字段來實現。
讀取出數據時,將此版本號一同讀出,之后更新時,對此版本號加一。此時,將提交數據的版本數據與數據庫表對應記錄的當前版本信息進行比對,如果提交的數據版本號大于數據庫表當前版本號,則予以更新,否則認為是過期數據。
對于上面修改用戶帳戶信息的例子而言,假設數據庫中帳戶信息表中有一個version字段,當前值為1;而當前帳戶余額字段(balance)為1000元。假設操作員A先更新完,操作員B后更新。
a、操作員A此時將其讀出(version=1),并從其帳戶余額中增加100(1000+100=1100)。
b、在操作員A操作的過程中,操作員B也讀入此用戶信息(version=1),并從其帳戶余額中扣除50(1000-50=950)。
c、操作員A完成了修改工作,將數據版本號加一(version=2),連同帳戶增加后余額(balance=1100),提交至數據庫更新,此時由于提交數據版本大于數據庫記錄當前版本,數據被更新,數據庫記錄version更新為2。
d、操作員B完成了操作,也將版本號加一(version=2)試圖向數據庫提交數據(balance=950),但此時比對數據庫記錄版本時發現,操作員B提交的數據版本號為2,數據庫記錄當前版本也為2,不滿足 “提交版本必須大于記錄當前版本才能執行更新 “的樂觀鎖策略,因此,操作員B的提交被駁回。
這樣,就避免了操作員B用基于version=1的舊數據修改的結果覆蓋操作員A的操作結果的可能。
操作員A操作如下:
select id, balance, version from account where id="1"; 查詢結果:id=1, balance=1000, version=1 update account set balance=balance+100, version=version+1 where id="1" and version=1 select id, balance, version from account where id="1"; 查詢結果:id=1, balance=1100, version=2
操作員B操作如下:
select id, balance, version from account where id="1"; 查詢結果:id=1, balance=1000, version=1 #操作員A已修改成功,實際account.balance=1100、account.version=2,操作員B也將版本號加一(version=2)試圖向數據庫提交數據(balance=950),但此時比對數據庫記錄版本時發現,操作員B提交的數據版本號為2,數據庫記錄當前版本也為2,不滿足 “提交版本必須大于記錄當前版本才能執行更新 “的樂觀鎖策略,因此,操作員B的提交被駁回。 update account set balance=balance-50, version=version+1 where id="1" and version=1 select id, balance, version from account where id="1"; 查詢結果:id=1, balance=1100, version=2
Hibernate、JPA等ORM框架或者實現,是使用版本號,再判斷UPDATE后返回的數值
到此,相信大家對“JPA怎么使用樂觀鎖應對高并發方式”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。