搜尋

首頁  >  問答  >  主體

redis - 防同一用戶並發操作方案諮詢

新客領取優惠券邏輯

同一用戶並發領券的情況下 會有超領的情況 因為並發進來時查詢資料庫均沒有領取記錄

而又不能針對用戶和優惠券做唯一索引 因為有些券允許用戶領取多張。

於是決定採用redis計數來防同一用戶並發超領 新客領券邏輯改成如下

上述邏輯沒有問題 但糾結該如何設定計數key的過期時間

有沒其他防並發領取方案呢? 可以不用糾結這種過期時間的設定。

滿天的星座滿天的星座2775 天前867

全部回覆(5)我來回復

  • PHP中文网

    PHP中文网2017-04-27 09:04:40

    建議將防併發超領與判斷是否新客 分開判斷

    • 防並發超領可以使用以下(上鎖), 每次操作完成記得刪除key(釋放鎖)

    // 操作的原子性,如该key在有效时间30秒被设置过返回0,一般请求超时为30秒
    $redis->set($key, 1, array("NX", "EX"=>'30'));
    • 判斷是否是新客(用戶是否領取過,有效時間設定到活動結束時間),可以設定一個新的KEY作用戶的領取狀態,領取過直接返回給用戶已經領取過優惠(追求並發性能時,不連接資料庫查詢)

    • 獲取優惠卷(提前把優惠卷塞入隊列,從隊列裡獲取,不從數據庫獲取,此處的好處是,隊列數據取完就沒,不會出現超領取情況),寫入redis隊列

    • 後台進程消費隊列,我用鍊錶阻塞類型進行消費(將領取資訊存放入庫,入庫前對用戶是否領取過優惠卷進行查詢判斷)

    回覆
    0
  • 黄舟

    黄舟2017-04-27 09:04:40

    具體的方案還是需要根據業務來實施

    1. 如果業務沒有要求100%,每個人只能領一張券,並且你的Redis集群是高可用的.那麼用Redis來擋就足夠了,具體Key的失效時間,等活動下載再整體失效,因為這是一個不落庫的方案,所以如果需要判斷是否重複請求需要依賴Redis裡面的數據(萬一Redis掛了呢,雖然Redis有比價不完善的持久化功能,但是丟的數據量比較少,如果如果業務上允許,還是可以接受的),但是很多場景業務都需要要求落庫

    2. 業務上要求,必須一人一券(可能券的面值很大),那麼前面還是用題主的Redis方案,在判斷完是第一次以後,直接插/更新數據庫(這裡就會有異步的方案,但是異步的方案也會出現,異步的方案也會出現與1類似的問題:丟數據),按照這個方案,可以保證每個人只能領一張券(由數據庫來保證數據的一致性) 。如題主所說,其實你不需要把Redis裡面的東西失效掉,因為如果一個用戶重複惡意請求抽券,直接走Redis來擋,不用去數據庫查詢(這裡還會一個問題,該方案是先更新緩存再更新資料庫,所以需要考慮快取更新成功,但是資料庫更新失敗的異常情況,如果資料庫異常需要清理該Key對應的快取)

    3. 如果實在扛不住(硬體跟不上),可以用隊列來解決,用隊列來削峰

    4. @大嗚 的方案也是可以行的,但是需要考慮某一時刻Redis這種異常情況

    回覆
    0
  • 滿天的星座

    滿天的星座2017-04-27 09:04:40

    @大嗚 提到的功能其實就是類似秒殺,我以前寫過一篇文章。你可以看看:
    電商類產品的必備功能:秒殺與競標

    回覆
    0
  • 滿天的星座

    滿天的星座2017-04-27 09:04:40

    寫隊列不行嗎

    回覆
    0
  • 淡淡烟草味

    淡淡烟草味2017-04-27 09:04:40

    這種事情你敢用nosql資料庫我也是醉了。

    建議先學學資料庫的理論,重點看看事務概念、事務的幾種隔離等級、行鎖表鎖,然後用傳統關係型資料庫來做這個事情。

    如果一台資料庫伺服器的效能不夠,那就設計成水平分錶架構,不同的伺服器,每台伺服器承擔一部分使用者資料。水平分錶的理論來源是本科層級的資料結構的hash章節,你可以補習一下。

    回覆
    0
  • 取消回覆