基于Lua脚本解决多条命令原子性问题

上一篇说了删锁存在原子性问题:

什么是原子性

        简单理解就是一件事情要么不做,要做就做全做好

  上一篇代码完成了基于redis 的setnx和加过期时间实现了分布式锁,但是因为判断锁是否一致和删锁是要执行两段代码,假如说我判断这是我这个线程的锁,好准备要删锁了,这时候服务宕机了或者停电了,是不是这个锁不是正常情况下释放的,虽然我们有过期时间兜底,那如果我们再换一种场景这个时候我们因为网络慢或者业务执行过长,导致锁到过期时间自动释放了,这时候我们第二个线程进来了他获取到锁了,准备开始执行业务操作了,但是我们上一个业务执行完了,好,他是不是要执行删除操作了,这时候问题来了,这个时候这把锁已经不是他自己的了,删掉别人的锁了,这个就是原子性问题。下面这个图简单就能很好的说明。

 

下面我们就要说到lua脚本了,因为他满足原子性操作。

什么是Lua脚本?

RedisTemplate已经为我们提供了API我们只要调用下面这个方法即可。

 

其中三个参数的含义是:script 就是你要执行的Lua脚本,keys是你要传入的锁的name,最后一个我们这里要传入的线程标识。

相关代码的实现

local voucherId=ARGV[1]
local userId=ARGV[2]

local storyKey='seckill:stock:'..voucherId
local orderKey='seckill:order:'..voucherId

--如果库存不足
if (tonumber(redis.call('get',storyKey))<=0) then
    return 1
end
--判断是否已经下单
if (redis.call('SISMEMBER', orderKey, userId)==1) then
    return 2
end
--两个条件都通过,扣库存,下单
redis.call('incrby',storyKey,-1)
redis.call('sadd',orderKey,userId)
return 0
//初始化lua脚本
    private static final DefaultRedisScript<Long> SECKILL_SCRIPT;
    static {
        SECKILL_SCRIPT = new DefaultRedisScript<>();
        SECKILL_SCRIPT.setLocation(new ClassPathResource("seckill.lua"));
        SECKILL_SCRIPT.setResultType(Long.class);
    }

 调用RedisTemplate中的excute方法

Long result = stringRedisTemplate.execute(
                SECKILL_SCRIPT,
                Collections.emptyList(),
                voucherId.toString(), userId.toString());

总结:

其实基于setnx加过期时间,已经算比较完备的解决分布式锁的解决方案了,但是其中还是其他问题

 

还会有哪些问题呢?下一篇我们来看看。

其中如有错误,请大家指出谢谢!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值