redis模拟秒杀解决库存剩余问题(三)

在模拟秒杀第二篇中,我们使用了redis事务,并用watch进行监控,虽然将超卖问题解决了,但是引出了一个新的问题——库存剩余。其中原因就是watch的乐观锁导致的,版本不一致,不执行事务。现在使用另外一个方案(即LUA脚本)进行解决。
LUA脚本是一个嵌入式的脚本,不能独立运行,需要其他的程序进行调用,本身是用C语言进行编写的,调用redis执行具有原子性。
在秒杀过程中,将redis的库存判断,秒杀过程,全部交给脚本实现,java代码中,只做调用
实现代码如下:


import com.lixl.redis.utils.RandomUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.scripting.support.StaticScriptSource;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;

/**
 * @author lixl
 * @description 秒杀相关controller
 * @date 2022/2/10
 */
@RestController
public class SecKillController {

    @Autowired
    private RedisTemplate redisTemplate;

    @RequestMapping("doSecKill")
    public String doSeckill(String goodId){
        String userId = RandomUtils.getUserId()+"";
        if (!StringUtils.hasText(goodId)){
            System.out.println("没有该类商品的秒杀");
            return "没有该类商品的秒杀";
        }
        List<String> keys = new ArrayList<>();
        keys.add(userId);
        keys.add(goodId);
        //调用lua脚本并执行
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
        redisScript.setResultType(Long.class);//返回类型是Long
        //lua文件存放在resources目录下的redis文件夹内
        redisScript.setScriptSource(new StaticScriptSource(getLuaText()));
        Object execute = redisTemplate.execute(redisScript, keys);
        System.out.println(execute.toString());
        return execute.toString();
    }

    /**
     * 组合lua脚本,执行redis操作
     * lua脚本执行redis操作时候,具有原子性,整体操作结束后,才会允许其他线程访问
     * @return
     */
    private String getLuaText(){
        // 定义变量userid设置值为key列表中第一个
        String text = "local userid=KEYS[1];\n" +
                // 定义变量goodid设置值为key列表中第二个
                "local goodid=KEYS[2];\n" +
                // 定义变量 kckey 为字符串
                "local kckey='good:'..goodid..':kc';\n" +
                // 定义变量 userkey 为字符串
                "local userkey='good:'..goodid..':user';\n" +
                // 利用redis调用查看set集合值方法 sismember
                "local userExists=redis.call('sismember',userkey,userid);\n" +
                // 判断如果值存在,则返回 2 不存在向下执行
                "if tonumber(userExists)==1 then \n" +
                " return 2;\n" +
                "end \n" +
                // 利用redis调用get方法,获取库存数量
                "local num=redis.call('get',kckey);\n" +
                // 判断库存数量小于 0 则返回 0 标识无法秒杀
                "if tonumber(num)<=0 then \n" +
                " return 0;\n" +
                "else \n" +
                // 调用减一方法,对库存量减一操作
                " redis.call('decr',kckey);\n" +
                // 设置秒杀成功用户id
                " redis.call('sadd',userkey,userid);\n" +
                "end \n" +
                // 秒杀成功 返回1
                "return 1";
        return text;
    };

}

至此完成秒杀问题

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值