redis+lua优惠券秒杀demo

栏目: Lua · 发布时间: 7年前

内容简介:目标是实现高并发的优惠券秒杀系统,项目地址:主要基于redis内嵌lua来实现库存的幂等性扣减,这块代码如下:

目标是实现高并发的优惠券秒杀系统,项目地址: https://github.com/owenliang/redis-lua

redis+lua优惠券秒杀demo

redis+lua

主要基于 redis 内嵌 lua 来实现库存的幂等性扣减,这块代码如下:

local quan_id = tostring(KEYS[1])
local uid = tostring(ARGV[1])
 
-- 应答函数
local function response(errno, msg, data) 
    errno = errno or 0
    msg = msg or ""
    data = data or {}
    return cjson.encode({errno = errno, msg = msg, data = data})
end
 
-- 判断用户没有抢过该优惠券
local log_key = "LOG_{" .. quan_id .. "}"
-- return log_key
local has_fetched = redis.call("sIsMember", log_key, uid)
if (has_fetched ~= 0) then
    return response(-1, "已经领取过")
end
 
-- 遍历优惠券所有批次
local quan_key = "QUAN_{" .. quan_id .. "}"
local batch_list = redis.call("hGetAll", quan_key)
local result = false
for batch_idx = 1, #batch_list, 2 do
    repeat
        -- 校验批次状态(是否online)
        local batch_info = cjson.decode(batch_list[batch_idx + 1])
        if (batch_info["online"] ~= true) then
            break
        end
 
        -- 尝试从券池取出1个券码
        local batch_key = batch_list[batch_idx]
        local coupon = redis.call("zRange", batch_key, 0, 0)
        if (#coupon == 0) then
            break
        end
        coupon = coupon[1]
        redis.call("zRem", batch_key, coupon)
 
        -- 弹出一个券码, 标记用户已抢
        redis.call("sAdd", log_key, uid)
 
        -- 将券码放入异步队列
        result = {uid = uid, quanId = quan_id, batchKey = batch_key, coupon = coupon}
        redis.call("rPush", "DB_QUEUE", cjson.encode(result))
    until true
 
    if result ~= false then
        break
    end
end
 
if (result == false) then
    return response(-1, "优惠券已抢完")
else
    return response(0, "秒杀成功", result)
end

脚本懒惰加载

当执行evalsha发现报错的情况下,则执行一次script load命令上传lua脚本,可以简化脚本上传的工作。

 public static function fetchFromRedis($uid, $quanId)
    {
        // 出于性能考虑, sha1请提配置到程序中
        $scriptSha1 = '8c2bdb856cefb05f39dc675c0fde28ef4c7bb0bd';
 
        if (0) {
        // 加载lua脚本
        $script = file_get_contents(__DIR__ . '/QuanFetch.lua');
 
        // 计算lua的sha1哈希
        $scriptSha1 = sha1($script);
        }
 
        $redisMaster = Redis::master('default');
 
        // evalsha执行脚本, 完成秒杀
        $result = $redisMaster->evalSha($scriptSha1, [$quanId, $uid], 1); // 按quanid做路由
        if ($redisMaster->getLastError()) { // 如果evalsha报错, 则进行一次script load
            // 懒惰加载lua脚本
            $script = file_get_contents(__DIR__ . '/QuanFetch.lua');
 
            if (!$redisMaster->script('load', $script)) {
                return false;
            }
            // 然后重试脚本
            $result = $redisMaster->evalSha($scriptSha1, [$quanId, $uid], 1);
        }
 
        $result = json_decode($result, true);
        return $result;
    }

开发与调试脚本

直接基于 PHP 调用lua很难调试脚本的问题,官方的redis-cli支持单步调试lua脚本,你可以自己学习一下: 《Redis Lua scripts debugger》。

我的开发流程是这样的:

编写lua脚本,然后命令行执行脚本:

redis-cli EVAL “$(cat /path/to/your/script.lua)” 1 “mykey” “myargv”

如果发现报错,则进行单步调试:

redis-cli –ldb –eval /path/to/your/script.lua mykey1 mykey2 , myargv1 myargv2

注意逗号之前的是key列表,之后的是argv列表,逗号两边需要留白。

使用起来就像gdb一样,n是下一步,p 变量名查看变量值,restart重新执行脚本,大家自己去挖掘更多吧。

压测脚本

先把脚本上传到redis,得到对应的SHA1值。

redis-cli SCRIPT LOAD “$(cat /path/to/your/script.lua)”

现在,执行压测:

redis-benchmark -r 100000000 -n 1000000 EVALSHA “8c2bdb856cefb05f39dc675c0fde28ef4c7bb0bd” 1 “mykey1” __rand_int__

-r指定了随机数的区间,__rand_int__用于随机数占位。

在这里,我让argv[1]随机生成,请求的次数是-n指定的,并发连接数使用-c(默认50)。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

The Zen of CSS Design

The Zen of CSS Design

Dave Shea、Molly E. Holzschlag / Peachpit Press / 2005-2-27 / USD 44.99

Proving once and for all that standards-compliant design does not equal dull design, this inspiring tome uses examples from the landmark CSS Zen Garden site as the foundation for discussions on how to......一起来看看 《The Zen of CSS Design》 这本书的介绍吧!

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

SHA 加密
SHA 加密

SHA 加密工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具