redis+lua优惠券秒杀demo

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

内容简介:目标是实现高并发的优惠券秒杀系统,项目地址:主要基于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)。


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

查看所有标签

猜你喜欢:

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

微积分的历程

微积分的历程

William Dunham / 李伯民、汪军、张怀勇 / 人民邮电出版社 / 2010-8 / 29.00元

“微积分”这一名称最早出现在哪本书中?第一本微积分教科书又是谁人所写?微积分究竟是谁人发明的?著名的洛必达法则居然是伯努利的研究成果?谁被誉为“分析学的化身”?谁又被誉为“现代分析学之父”?哪些数学天才使微积分的创建过程终于画上完美的句号?……本书将带你一一探究上述问题。 本书宛如一座陈列室,汇聚了十多位数学大师的杰作,当你徜徉其中时会对人类的想象力惊叹不已,当你离去时必然满怀对天才们的钦佩......一起来看看 《微积分的历程》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具