Java知識分享網 - 輕松學習從此開始!????

Java知識分享網

Java1234官方群25:java1234官方群25
Java1234官方群25:838462530
     

GIT視頻教程(結合github,碼云)免費領取

SpringBoot打造全棧在線教育平臺實戰課程

SpringBoot打造企業級進銷存

Java1234 VIP課程

領取微信掃碼登錄Java實現視頻教程

Java1234至尊VIP(特價活動)

“樂觀鎖”輕松搞定高并發下的冪等性問題實戰


分享到:
時間:2020-08-06 15:23來源:http://www.248787.tw 作者:小鋒  侵權舉報
“樂觀鎖”輕松搞定高并發下的冪等性問題實戰視頻教程
失效鏈接處理
“樂觀鎖”輕松搞定高并發下的冪等性問題實戰視頻教程


專題帖子
公眾號帖子

視頻教程

“樂觀鎖”輕松搞定高并發下的冪等性問題實戰視頻教程

相關截圖:




主要內容:

什么是冪等性?
冪等(idempotent、idempotence)是一個數學與計算機學概念,常見于抽象代數中。在編程中.一個冪等操作的特點是其任意多次執行所產生的影響均與一次執行的影響相同。冪等函數,或冪等方法,是指可以使用相同參數重復執行,并能獲得相同結果的函數。這些函數不會影響系統狀態,也不用擔心重復執行會對系統造成改變。例如,“getUsername()和setTrue()”函數就是一個冪等函數. 更復雜的操作冪等保證是利用唯一交易號(流水號)實現. 我的理解:冪等就是一個操作,不論執行多少次,產生的效果和返回的結果都是一樣的 。
高并發下的冪等性問題
這里以兩個實例來看下高并發下的冪等性問題;
 
一,購票實例
 
購票實現流程如下:
 
step1:查詢是否有票,有票的話,繼續下一步,否則提示無票,結束;
 
step2:從用戶賬戶扣除票款;
 
step3:余票減一操作;
 
這里的話,正常情況沒問題,但是比如用戶連續多點了幾次,或者網絡問題導致的再或者多人同時購買的時候的并發情況下,step1步驟會有兩個或者多個線程同時進入,這時候判斷都是有票的,然后繼續進入step2,step3,這時候,就可能會出現余票負數,多賣的情況;
 
二,充值實例
 
充值實現流程如下:
 
step1:用戶輸入充值金額,請求后端業務系統;
 
step2:后端生成訂單,訂單狀態是未支付,然后再請求第三方支付接口;
 
step3:用戶端確認支付;
 
step4:第三方支付通過我方提供的回調接口異步通知支付結果;
 
 
 
具體step4 demo代碼如下:
 
System.out.println("查詢訂單");
Order order = orderMapper.getByOrderId(orderId); // 根據訂單id獲取訂單
if(order.getStatus()==0){ // 假如是未支付狀態
  System.out.println("未支付狀態");
  order.setStatus(1); // 設置支付成功狀態
  System.out.println("更新支付狀態...");
  orderMapper.update(order); // 更新支付狀態
  System.out.println("賬戶充值...");
  userAccountMapper.addAmount(order.getAmount(),userAccount.getUserId()); // 賬戶充值
  System.out.println("充值完畢...");
  return true;
}else{ // 已經支付成功,訂單已處理
  System.out.println("發現訂單已處理");
  return true;
}
 
 
這個第四步是有缺陷的,假如第三方支付系統問題或者網絡問題,有多個線程同時執行進入 
 
Order order = orderMapper.getByOrderId(orderId);
 根據訂單id查詢訂單信息,發現status狀態都是未支付,所以都進入if里面,這時候就出現了賬戶重復充值的情況;
 
 
 
冪等性問題總結
只要更新數據是依賴讀取的數據作為基礎條件的,當遇到高并發的時候,就可能會出現冪等性問題;
又比如在更新數據不依賴查詢的數據的就不會有問題,例如修改用戶的名稱,多人同時修改,結果并不依賴于之前的用戶名字,這就不會有并發更新問題。
 
 
冪等性問題解決方案
關于冪等性問題的解決方案,業界提供了很多解決方案,如單機系統的Java 同步鎖,樂觀鎖,悲觀鎖,分布式鎖,唯一性索引,token機制防止頁面重復提交等,每種方案各有利弊;不過主流的話,還是樂觀鎖和分布式鎖這兩個方案;
 
 
 
Java同步鎖方案
我們可以使用synchronized同步鎖,把查詢狀態的代碼和更新的代碼放一個同步鎖內,這樣同一時刻只能有一個線程進入執行,等執行完其他線程才能進入,這樣能解決冪等性問題,但是假如同步塊里面的業務代碼執行時間比較長,這樣會嚴重影響用戶體驗,和系統的吞吐量。所以不是最佳方案;
 
 
 
悲觀鎖方案
悲觀鎖(Pessimistic Lock),顧名思義,就是很悲觀,每次去拿數據的時候都認為別人會修改,所以每次在拿數據的時候都會上鎖,這樣別人想拿這個數據就會block直到它拿到鎖。
 
悲觀鎖:假定會發生并發沖突,屏蔽一切可能違反數據完整性的操作。
 
Java synchronized 就屬于悲觀鎖的一種實現,每次線程要修改數據時都先獲得鎖,保證同一時刻只有一個線程能操作數據,其他線程則會被block。
 
數據庫的悲觀鎖通過 for update 實現的;
 
select * from t_order where orderId=#{orderId} for update
悲觀鎖使用時一般伴隨事務一起使用,數據鎖定時間可能會很長,影響用戶體驗和系統吞吐量,所以一般也不采用。
 
 
 
樂觀鎖方案
樂觀鎖(Optimistic Lock),顧名思義,就是很樂觀,每次去拿數據的時候都認為別人不會修改,所以不會上鎖,但是在提交更新的時候會判斷一下在此期間別人有沒有去更新這個數據。樂觀鎖適用于讀多寫少的應用場景,這樣可以提高吞吐量。
 
 
 
樂觀鎖:假設不會發生并發沖突,只在提交操作時檢查是否違反數據完整性。
 
 
 
樂觀鎖一般來說有以下2種方式: 
 
1. 使用數據版本(Version)記錄機制實現,這是樂觀鎖最常用的一種實現方式。何謂數據版本?即為數據增加一個版本標識,一般是通過為數據庫表增加一個數字類型的 “version” 字段來實現。當讀取數據時,將version字段的值一同讀出,數據每更新一次,對此version值加一。當我們提交更新的時候,判斷數據庫表對應記錄的當前版本信息與第一次取出來的version值進行比對,如果數據庫表當前版本號與第一次取出來的version值相等,則予以更新,否則認為是過期數據。 
 
2. 使用時間戳(timestamp)。樂觀鎖定的第二種實現方式和第一種差不多,同樣是在需要樂觀鎖控制的table中增加一個字段,名稱無所謂,字段類型使用時間戳(timestamp), 和上面的version類似,也是在更新提交的時候檢查當前數據庫中數據的時間戳和自己更新前取到的時間戳進行對比,如果一致則OK,否則就是版本沖突。
 
 
 
樂觀鎖方案在不影響系統性能的情況下,解決了高并發冪等性問題,所以被得到廣泛使用。唯一的缺點就是對代碼具有入侵性。
 
 
 
分布式鎖
對于分布式系統,多個系統獨立運行,所以同步鎖肯定是不行的;對于分布式系統,可以用樂觀鎖或者分布式鎖來解決冪等性問題;
 
具體方案有:
 
1. 基于緩存(Redis等)實現分布式鎖;
 
2. 基于Zookeeper實現分布式鎖;
 
(備注:下期我們會提供具體實現方案的視頻教程,感謝關注)
 
------分隔線----------------------------
  • 上一篇:沒有了
  • 下一篇:沒有了
關注Java1234微信公眾號
欄目列表
推薦資料
鋒哥公眾號


鋒哥微信


關注公眾號
【Java資料站】
回復 666
獲取 
66套java
從菜雞到大神
項目實戰課程
体育彩票开奖时间