一個遊戲撥賬系統的數據庫結算設計

假設現存在一個簡單的猜大小遊戲,由用戶下注大或者小,扣除手續費3%後的錢全部放入獎池中,贏的一方按投注比例平分整個獎池。使用mysql作爲數據庫,系統精度精確到1位小數。
本文將會講解其中會出現的業務結算導致的數據問題,以及解決方法。


數據庫邏輯設計

系統內應該存在一個用戶錢包表,其中指定兩條記錄爲系統收入賬戶和系統撥出賬戶。這樣可以將投注的時候,對系統賬戶餘額增加操作,和發獎的時候,對系統賬戶餘額的減去操作分離。

可以避免上一期遊戲的結算,對下一期遊戲的投注發生鎖等待的問題。

業務加鎖

考慮到高併發的情況下,推薦使用mysql自帶的排他鎖,不推薦樂觀鎖,因爲樂觀鎖需要重試機制,而隊列結算暫時不考慮。
當一名用戶發起投注的時候,檢查順序應該如下

  1. 檢查系統遊戲開關
  2. (冗餘) 查詢一次用戶餘額是否大於這次下注金額
  3. 開啓事務
  4. 對系統收入賬戶加排他鎖
  5. 對用戶收入賬戶加排他鎖
  6. 檢查用戶餘額是否足夠
  7. 對用戶進行扣款
  8. 對系統進行收款
  9. 爲獎池加入97%的投注額度
  10. 事務提交

這裏之所以要冗餘檢查用戶的額度,是否了避免開啓事務的消耗,防止惡意攻擊消耗系統資源,用來開啓無意義事務。

獎池額度的97%這裏計算需要保持一位精度,如果用戶投注是98,按照計算得到的值應該是95.06,我們應該取95.0而不是95.1,否則你最後存到獎池裏面的數就會大於97%,這樣系統抽取就不會達到3%,用戶少分點沒關係,要保證系統一定能分到3%。

簡單一句話就是:精度位後都捨棄

發獎過程設計

假設按照投注比例,瓜分出的獎金總數是22.1,A用戶的份額是55.5%,A用戶拿到12.2655,B用戶的份額是45%,B用戶拿到9.8345

這種情況下,你會發現,按照捨棄,原則,分別是12.29.8,結果是隻發放了22,如果你按照四捨五入原則,才能發放到22.1

那爲什麼還要堅持捨棄原則呢?因爲,假設出一個極端情況,當你碰到A的值是12.05,B的值是9.05,按照捨棄原則,總數的確還是22.1。但是按照四捨五入原則,發放的總值就是22.2了。

結語

在計算機系統內,浮點數的計算本身就是不可靠的,在業務內應該用整形去避免,當設計到百分比操作的時候,請儘量使用捨棄原則,保證不多發。按照捨棄原則,給用戶少發0.05這種精度外的值,對業務來說無關緊要。如果超發了,會導致系統內賬目混亂,後果將不堪設想。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章