爲什麼加了鎖還是出現庫存扣超的情況

最近碰到一個需求,庫存扣減,要保證在高併發情況下不出問題。

第一想到的就是redis分佈式鎖,爲了保證原子操作,加鎖和解鎖都使用lua腳本,但是在壓力測試的時候發現,TPS只有個位數,而且成功率不好控制,我是自己控制嘗試獲取鎖的次數,因此造成了大量的線程阻塞。

後面找了一些資料,改爲redisson,TPS能達到100以上,成功率在90%以上,以爲就此完結,後來自己做測試的時候發現數據總是有誤,這裏再提一下,業務並不是直接扣減庫存,而是預扣減,我的做法是增加一條預扣減庫存記錄,在確認扣減後再進行真正的庫存扣減,所以在每次有預扣減前先查詢預扣減記錄,用實際的庫存減去預扣減記錄之和後纔是剩下的庫存,但是在壓測的情況下(100個線程循環10次,測試工具爲jmeter),庫存明明只有一個,居然出現多餘一條的預扣減記錄,也就是說實際庫存是不夠減的,排除了鎖的問題,最後發現其實是插入預扣減庫存記錄的事務沒有及時提交(用的ORM是spring-data-jpa),也就是說,第一個線程進來,插入了一條預扣減庫存數據,但是並沒有立即提交這個事務,然後釋放鎖,第二個線程進來查詢預扣減數據並沒有查到第一個線程插入的那條數據,於是庫存數據就出錯了。

在網上找了很多資料,找到了一種解決方案,就是spring的事務監聽,等待事務提交後再釋放鎖,讓新的線程進來,就不會出錯了。

TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
                @Override
                public void afterCommit() {
                    lock.unlock();
                }
            });

把查詢與插入操作抽取到一個單獨的方法中並加上事務註解,然後加上上面的這段代碼,就可以保證在高併發下庫存的正確性。

當然,這個監聽也是個同步方法,會一直等待事務提交,所以性能上有些許的下降,但還能接受,至少在分佈式部署的情況下高併發也不會出現庫存錯誤的問題。

發佈了92 篇原創文章 · 獲贊 30 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章