Redis+Lua解決高併發場景搶購秒殺問題

以前寫了一篇PHP+Redis鏈表解決高併發下商品超賣問題,今天介紹一些如何使用PHP+Redis+Lua解決高併發下商品超賣問題。php

爲什麼要使用Lua腳本解決商品超賣的問題呢?html

  • Redis在2.6版本後原生支持Lua腳本功能,容許開發者使用Lua語言編寫腳本傳到Redis中執行。
  • 將複雜的或者多步的redis操做,寫爲一個腳本,一次提交給redis執行,減小反覆鏈接redis的次數,提高性能。
  • 原子操做。Redis會將整個腳本做爲一個總體執行,中間不會被其餘請求插入。所以在腳本運行過程當中無需擔憂會出現競態條件,無需使用事務。
  • 複用。客戶端發送的腳本會永久存在redis中,這樣其餘客戶端能夠複用這一腳本,而不須要使用代碼完成相同的邏輯。

首先,編寫lua腳本,腳本名爲secKill.lua:redis

-- 接收參數
local user_id  = KEYS[1]
local goods_id = KEYS[2]

-- 拼接字符串
local stock_key = "secKill:"..goods_id..":stock" -- 秒殺商品庫存key
local users_key = "secKill:"..goods_id..":users" -- 成功秒殺商品的用戶集合key

-- 判斷用戶是否已經成功秒殺過該商品,若是已經存在在集合中,說明已經成功秒殺該商品,直接返回標誌2,防止重複搶購
local user_exists = redis.call('sismember', users_key, user_id)
if tonumber(user_exists, 10) == 1 then
    return 2
end

-- 獲取當前商品庫存,若是庫存小於等於0,表名商品已經被搶購完了,不然庫存-1,並將搶購成功的用戶放入集合中
local left_goods_count = redis.call('get', stock_key)
if tonumber(left_goods_count, 10) <= 0 then
    return 0
else
    redis.call('decr', stock_key)
    redis.call('sadd', users_key, user_id)
end
return 1

上述代碼中返回的數字0,1,2只是一種約定,本身能夠根據本身的有業務約定不一樣狀態返回的值。示例代碼0:庫存爲0,1:秒殺成功,2:已秒殺成功的用戶重複搶購。shell

lua腳本編寫完成後,使用redis-cli命令生成該腳本的sha祕鑰數據庫

redis-cli script load "$(cat /usr/local/redis/lua/secKill.lua)"
"63454a53284d9f6b30bdb6e5e12796a74f61f718"

最後,拿到lua腳本的sha祕鑰,咱們就能夠在咱們的代碼中使用了。併發

$redis = new Redis();
$redis->connect("192.168.111.128", 6379);
$goodsId = 11211;
$userId = mt_rand(10000, 99999);
$res = $redis->evalSha('63454a53284d9f6b30bdb6e5e12796a74f61f718', [$userId, $goodsId], 2);

能夠看到,咱們將搶購邏輯寫到lua腳本後,PHP代碼就變得不多了,僅僅只有5行代碼。memcached

編寫好代碼,接着咱們開始對上述代碼進行測試。高併發

首先,咱們須要設置商品的庫存量,正常邏輯是在後臺商品管理頁填寫具體商品的庫存量,此處假設咱們的商品ID是11211(這個數字是否是很熟悉?是的,這是memcached的默認端口),商品數量爲10個。工具

$redis-cli
> set secKill:11211:stock 10

咱們使用ab壓測工具模擬2000個用戶併發量200來模擬搶購商品ID爲11211的商品。性能

$ ab -n 2000 -c 200 http://www.master.com/index.php

若是沒有ab工具須要使用 yum -y install httpd-tools安裝

壓測完成後,咱們經過RedisDesktopManager(RDM)軟件來查看搶購結果,能夠看到即便是200的併發量,最終也只有10個用戶搶購到商品,而且搶購成功的用戶被寫入到了secKill:11211:users的集合中,咱們能夠另外開一個守護進程專門用於從集合中獲取用戶ID處理後續事宜(將數據落盤寫入數據庫、給用戶發短信等)

使用Redis+Lua來解決搶購秒殺類問題是當前比較流行的一種作法,但願對正在開發秒殺搶購功能的你能產生幫助。

相關文章
相關標籤/搜索