PHP 使用 Redis 隊列製做秒殺搶購的初步實現與思考

先說一下我是如何一步步被逼使用Redis去解決秒殺的問題的 !前端

最開始沒有考慮高併發庫存超賣這些問題,簡單的實現一套訂單系統,結果是悲劇的,前臺還傻X似的查商品庫存顯示,活動開始後都變成負數了 … 老臉一橫,找解決方案數據庫

最先的時候使用一些限流手段,好比輸入驗證碼啊,受權搶購啊,以期下降併發數,但整體體驗太差,遂放棄服務器

以後使用文件鎖之類的解決方案,發現根本很差使,當文件被寫入鎖住的時候,實際上併發已經發生了 …併發

不得已採用 Redis , 開始的時候,將商品庫存存入 List ,而後下單從 List 中 Pop 庫存出來,而後入庫減庫存 !! 這套方案能夠確保庫存不超賣,可是有幾點遺憾異步

一、用戶搶購成功,可是不支付;這個時候就須要將訂單按期回收,這就形成搶購一開始不少人搶不到,過一會撿漏卻是很輕鬆 !
二、搶購成功即生成單號入庫,由於還有一些列操做(生成單號、生成二維碼、發送下單通知 …),併發的時候會形成系統卡住 !就是活動開始那幾秒,整個項目是卡死的 …
三、以上兩點綜合起來,就是搶購成功,頁面無響應,只能一直等待,用戶不知道狀況,頁面也一直沒響應,結果就關閉了,而後訂單建立好頁面響應成功了訂單又被回收了 … 死循環高併發

—————— 很糟糕的體驗性能

後來我又繼續找解決方案,發現 RabbitMq 隊列,大爲驚喜,我只須要將 Pop成功的用戶隊列起來,而後定時消費入庫,就行了 !!
說歸說,實現起來各類想不通,總結以下,期待有經驗的大神回覆 !
Win 環境,成功安裝並啓動 ERLANG、RabbitMq,成功安裝 AMQP - PHP擴展隊列

一、根據官方文檔,當我得到庫存Pop資格後,將用戶及用戶搶購的商品信息以隊列的形式提交到 Rmq,這個併發的過程,投遞失敗及其餘情況不知道怎麼捕捉
二、消費者端不知道如何部署,假如是定時任務,輪詢某個URL,而後該URL負責消費的話,會產生太多太多消費者,服務器會拖死,網上有說作個 RD 鎖,即當存在消費的時候,直接Return,這個機制不知道是否合適
三、當消費成功,應答成功後,如何通知前端 ?消費成功後將訂單號和用戶ID,寫入 Redis Hash散列 ?前端輪詢 ?文檔

------------------- 搞不顛,但願有大神能說說這個機制 ,完整詳細一點的部署

最終選擇經過 Redis 隊列實現併發訂單,大體實現思路以下

一、搶購時 Pop 庫存隊列,成功則寫入用戶即商品信息到一個 Hash 散列,(同時:將用戶ID PUSH 到一個 List),前端進入排隊頁,失敗直接進入失敗頁 (確保不超賣)
二、定時任務消費隊列,經過 LLEN 判斷用戶LIST,而後根據用戶ID消費 Hash 鍵,生成訂單入庫,(同時寫入訂單信息到一個 Hash散列,一樣以用戶ID爲鍵)
這兩部能實現庫存不超賣、響應很快
三、前端搶購成功排隊頁輪詢,根據本身ID,hGet 散列,當拿到數據,進入支付頁,生成預支付參數 !
四、定時任務輪詢數據庫訂單,必定時間未支付的訂單,關閉 (再也不回收庫存)
五、支付異步通知,減庫存
六、每日定時輪詢商品表,若是有庫存,從新生成庫存 List ,前端可再次搶購

以上爲當前實現的邏輯,箇中確定有不少不理想的地方,我本身能考慮到的有
一、Redis 性能問題,它一旦崩潰了怎麼辦,整個數據鏈就斷了
二、輪詢異常怎麼辦,進入排隊頁遲遲獲取不到訂單,假使給個輪詢時間閾值,這個值沒有合理時間啊 …

其餘還有不少地方不足,期待大神指出 !

相關文章
相關標籤/搜索