引言
多個進程或線程同時(或著說在同一段時間內)訪問同一資源會產生并發問題。
?
銀行兩操作員同時操作同一賬戶就是典型的例子。比如A、B操作員同時讀取一余額為1000元的賬戶,A操作員為該賬戶增加100元,B操作員同時為該賬戶減去 50元,A先提交,B后提交。 最后實際賬戶余額為1000-50=950元,但本該為1000+100-50=1050。這就是典型的并發問題。
?
從事零售供應鏈庫存業務,對庫存數量操作增減十分頻繁,同樣存在類似上述銀行取款遇到的問題,庫存數量操作有誤勢必給前臺銷售產生損失影響,因此需要關注對庫存數量并發操作下的一致性。
?
下面通過一個真實的案例分享在并發情況下如何保證庫存數量的準確性。
問題是什么-加鎖失效
看看下面這段流程和代碼,思考會有并發問題嗎?
?
1.加鎖前,獲取箱子明細數據,此處在鎖之外,存在并發臟讀問題
?
2.加鎖后,并進行箱子上架分批次回傳業務處理
?
3.加鎖后,更新箱子明細上架數量邏輯:已上架數量 = 加鎖前的明細數據(臟讀) + 報文回傳的明細數據 直接進行行更新
原因是什么-加鎖的位置不正確
核心的問題原因
1.業務分布式鎖失效:使用分布式鎖加鎖了,但是仍然使用加鎖前查詢的數據,導致出現臟讀
2.Mysql鎖失效:數據庫更新時,未上任何鎖,導致臟讀的數據直接覆蓋更新當前行
?
有同學這時問了,為啥防重碼也沒有生效呢?
防重碼主要是用作冪等邏輯的,同一個請求多次處理,結果仍然是相同的。
但是這是兩次不同的請求,防重碼是不同的,因此不能只依賴防重碼保證一致性。
?
解決方案有哪些
1、代碼層面:使用鎖(如互斥鎖、讀寫鎖、分布式鎖等)來控制資源的訪問,數據獲取的全部操作都需要再獲取鎖后才進行。
將獲取箱子明細的代碼移動到加鎖之后,只有獲取到分布式鎖,才能執行分批次上架查詢和更新(串行化)
對應改造后的代碼:
?
2、數據庫層面:實現事務管理,確保數據的一致性;合理設置事務隔離級別,以防止臟讀、或者采用樂觀鎖或悲觀鎖來處理并發更新,合理設計查詢效率,減少鎖競爭。
數據庫的并發上鎖處理和業務代碼的上鎖是互補的關系
因為無法保證后續業務的調整或其他業務代碼的調用能始終保持獲取數據的一致性,數據庫的并發上鎖處理更多是一種兜底保證機制。
?
樂觀鎖更新
?
悲觀鎖更新
?
擴展方案
1.應用程序設計: 在應用程序設計階段,盡量避免長時間持有數據庫連接或事務,減少并發操作的可能性,利用AI代碼評審或者人工提前找出可能出現并發問題的地方;合理設置鎖的粒度,避免鎖失效。
?
1. 網絡負載層面:采用限流控制訪問頻率;采用分布式數據庫,進行數據分片,降低單節點并發壓力;使用負載均衡,將網絡請求分發到不同的服務器,提高系統處理并發的能力,防止系統過載。
?
1.請求層面:前端點擊防重、系統冪等防重、盡可能降低同一請求的多次重試訪問引起的一致性問題。
?
通過以上措施,可以在不同層面有效地防止并發問題,保證系統的數據的一致性
審核編輯 黃宇
-
分布式
+關注
關注
1文章
903瀏覽量
74536 -
代碼
+關注
關注
30文章
4791瀏覽量
68677
發布評論請先 登錄
相關推薦
評論