文章目錄
下面是這周遇到的一個並發的問題,雖然沒有造成什麼線上的問題,不過感覺還危險的,記錄下,以免以後再出現類似的問題。
現象簡介:
需求預發布驗證的時候發現積分計算不正確。
問題定位:
首先尋找應用所有相關的日誌,沒有有用的資訊。積分計算這裡也沒有錯誤記錄檔,說明不是程式報錯引起的,而算分這裡的邏輯之前項目中有詳細的驗證過,應該不會有問題。
好在積分計算這裡有詳細的日誌,把兩邊日誌進行詳細的比對,發現應用對所有的處理都已經正確的進行調用,而處理方也已經正確的接受到所有的調用,所以定位到是處理方接受到調用之後處理的時候出現的問題。
問題分析:
這是審核記錄之後的處理過程(簡化),在沒有並發的時候是正確的。
在批量審核的時候,因為是通過一個for迴圈,每次審核一條記錄。而積分計算這裡是通過非同步線程來完成的,這樣一個批量審核的積分計算可能會成為下面這個圖裡面這樣
假設這幾個積分變動事件都是對會員A的憑證+1,則最後的處理可能是下面這個樣子,兩次+1操作,最後的結果卻僅加了1,錯誤出現。
解決方案
1. 對這個代碼模組增加synchronized(this):這樣會導致這個方法完全成了串列的,不同memberId也不可以並存執行,影響有點大
2. 用cache來解決這個問題,類似ConcurrentLock:不過這個是在credit裡面,他們要用的話還得再搞一套
3. 直接用資料庫的update xx set num=num+1這樣的sql語句來保證不出現髒讀和資料覆蓋的問題。
最後確定使用第三種解決方案,一方面完全可以滿足這裡的需求且影響較小,另一方面改動成本也比較小。
Ps:
oracle在處理update xx set num=num+1之類的語句時,會加一個行級排它鎖,保證在操作期間不會有並發問題。
這個自己實驗了一下,啟動兩個用戶端連到資料庫,其中一個執行這樣的語句,然後不commit,另一個用戶端在執行同樣的語句,就會一直卡在那裡,直到前一個被commit。
After All
這個問題主要原因是沒有考慮到並髮帶來的問題。希望大家以後遇到類似情境的時候可以考慮一下這些問題,tks!