Redis实战之使用Lua脚本保证原子性

Posted 程序员历险记

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Redis实战之使用Lua脚本保证原子性相关的知识,希望对你有一定的参考价值。

    Redis,高性能读写缓存服务,互联网高并发利器之一。认识他的时候还是在4年半前,那时候刚毕业不久,做着传统企业项目,和同事们分享了一下这个东西,同事们不以为然,现在互联网项目都会把他列入项目技术栈之一吧。

    第一次实际使用在系统中的时候用来记录token进行session共享,随后有应用于短信验证码的有效期中,后续有用于小数据的缓存,列如省市区数据,App首页自定义配置数据缓存等等,又应用于定时任务的全局锁,提交订单的全局锁,单品秒杀等等业务场景。。

    这时候才发现Redis真的是一把神器,没他真的不好办事情。


    说到原子性,大家都知道,要么都成功,要么都失败,那么如果我的数据保存在Redis里面,我怎么保证两个命令的原子性呢?Redis在2.6版本以后提供了Lua脚本的支持。

    

    有人会说我不会Lua脚本啊。不要慌,我也不会,但是我也就花了一个小时不到了解了一下基础的语法,已经足够应付基础的开发使用。


    好了,这时候假设自己已经会Lua脚本了。Redis实战之使用Lua脚本保证原子性Redis实战之使用Lua脚本保证原子性Redis实战之使用Lua脚本保证原子性


    那么现在可以举栗子了Redis实战之使用Lua脚本保证原子性Redis实战之使用Lua脚本保证原子性


    现在有A,B,C,D四个SKU(商品),A库存20,其他库存100,现在有两个订单M(A-12,B-50),N(A-15,B-50)(M订单A购买12份,B购买50份)

    按照一般的思路就是通过Redis的decrby命令进行扣减库存,首先扣减M订单没有问题。接着扣N订单,如果先扣B商品库存,这时候正好有订单K需要购买了商品B扣减1份,结果K订单扣下来发现B商品库存小余0了,所以K订单无法下单,这时候N订单的线程扣A商品15份,发现库存不足,这时候需要把之前扣的B50份有还回去,这就造成了明明K订单是满足要求的,但是因为线程之间的时间差导致了K订单无法创建,商品库存足够商品却卖不掉。

    这时候Lua脚本就可以上场了,Redis保证了只能有一条指令在执行,所以我们可以把扣减多个商品的指令合并成一个指令,那不就可以了?

    

    Lua脚本上场:

    

String script = "local b = 1 " +

            " local n = table.getn(KEYS) " +

            " for i=1,n,1 do " +

            " if redis.call('get',KEYS[i]) >= ARGV[i] then b=1 else b=0 break  end " +

            " end " +

            " if(b>0) then " +

            " for i=1,n,1 do " +

            " redis.call('decrby',KEYS[i],ARGV[i]) " +

            " end return 1 else return 0 " +

            " end ";


jedis.eval(script,Arrays.asList(key1,key2),Arrays.asList(decyCount,decyCount));


以上脚本的大体意思就是获取key的数量,循环检查库存是否满足,不满足break return 0,满足就开始扣库存,return 1.


    没错,就是这么简单,M订单处理完了,处理N订单,接着处理K订单。


    以上当我们需要保证Redis多个指令的原子性,可以试着使用Lua脚本,但是注意一点的是,脚本千万不要太长逻辑太复杂哦。

   





以上是关于Redis实战之使用Lua脚本保证原子性的主要内容,如果未能解决你的问题,请参考以下文章

Redis_07_Lua脚本实现多条Redis命令原子性

Redis_05_Lua脚本实现多条Redis命令原子性

Lua 解决 Redis 缓存原子性问题

Lua 解决 Redis 缓存原子性问题

redis原子性读写操作

REDIS09_分布式锁的概述加锁使用sexnu解锁使用lua脚本保证原子性引发的问题思考