Redis 實現秒殺

導語

秒殺想必你們都瞭解,在短期內請求訪問會激增,同時要保證不會超賣和數據的準確,對於技術方面仍是有些考驗的。惋惜的是,一直沒有機會在項目中實現。再看了一些資料後,打算實驗下。如下代碼僅爲測試所用,環境比較簡單,請根據實際狀況進行修改。php

建立秒殺隊列

在開始秒殺以前,先將商品放入隊列中,以下html

/**
     * 建立秒殺列表
     */
    public function createList()
    {
        $count = 30;
        $redisKey = 'goods_list';

        for ($i = 1; $i <= $count; $i++) {

            // 測試用,防止數據錯誤
            if (Redis::llen($redisKey) >= $count) {
                break;
            }

            Redis::rpush($redisKey, $i);
        }
    }

執行完後,在 Redis 中看下laravel

商品隊列

有 30 個商品 ID,數據正常。redis

秒殺

接下來是關鍵的一步,使用的是 Redis 的 lpop 命令獲取商品 ID,利用的是 Redis 的原子性。sql

/**
     * 秒殺
     */
    public function buy()
    {
        // 隨機用戶名,無心義,僅作標記
        $username = Hash::make(now());

        if ($goodsId = Redis::lpop('goods_list')) {
            // 購買成功
            Redis::hset('buy_success', $goodsId, $username);
        } else {
            // 購買失敗
            Redis::incr('buy_fail');
        }
    }

如上,簡化了代碼,購買以後,成功與否只是作記錄。實際應用中,固然會更加複雜,但要注意的是,不要同步操做 Mysql。多說一句,Hash:make(now()) 即便值相同,也不會生成相同的數據,參考這裏segmentfault

測試

最後就是進行測試了,使用 ab 測試,執行 ab -c 300 -n 3000 http://localhost/buy/ ,上述命令的意思是 300 併發,共請求 3000 次架構

buy_success

執行完成,速度並不快,而且還有 794 個訪問失敗。來看下數據是否正確吧。在頁面中打印 buy_success併發

buy_success

30 個成功者。再來看下秒殺失敗的數量高併發

buy_fail

不是一個準確的數字,2165+30 是全部請求成功的數字,再加上失敗的 794 ,總數是 2989,依然不足 3000。post

結語

上述測試有不足的地方,響應速度慢、請求失敗、失敗計數不許確。看來有不少要優化的地方,不止是代碼層。測試的時候忘記將訪問記錄入庫關掉,應該是有些影響。
好的方面是秒殺成功的數量是準確的,沒有超賣。

更新

從新配置了環境, 測試了一樣的代碼。不一樣的地方是增長了每次訪問判斷是否在黑名單(實時查詢 MySQl),而訪問記錄改爲了隊列寫入 MySQL。結果以下
ab 測試
對比來看仍是有所提高的。最主要的是數據終因而正確的,訪問失敗 355,搶購成功 30,搶購失敗 2615(忘記截圖了),相加總數正好是請求總數。並且 MySQL 中記錄的訪問記錄也是 3000。


參考資料:Redis實現高併發下的搶購、秒殺功能基於雲原生的秒殺系統設計思路秒殺架構設計

相關文章
相關標籤/搜索