通过redis+lua实现加减库存

sundy0条评论 32 次浏览

一. 场景

下单后库存校验或者秒杀场景下,有很多利用“锁”的方案来解决问题。但是加锁其实是一件性价比很低的事,所以我们采用用redis+lua的方式来实现这个功能。

二. 思路

阶段一
在库存加减逻辑中分为2个步骤:STEP1.读取库存STEP2.读取库存
利用其他方法例如”锁”等,也就是想控制好STEP2一定要紧跟STEP1,本质上就是确保获取的库存的最新的数据为最新。
阶段二
在相对较高的并发场景下,redis被常用作库存管理,我们需要通过最小成本的改动来实现库存的限制。但是redis的读取库存+读取库存一般都是有上层应用代码控制,有没有办法在一个函数调用中能串行执行这俩个步骤了?
阶段三
众所周知,redis是单线程的,并且现在已经支持lua脚本,那是不是可以利用该组合实现我们的场景了?

三. 设计方案

这个方案就很简单了,直接利用redistemplate执行lua脚本

四. 代码

4.1 lua脚本代码样例

    private static final String GET_COUPON_CODE =             "local values = redis.call('hmget',KEYS[1],'recvCnt','couponCnt');n" +  //lua返回value的数组                     "if tonumber(values[1]) < tonumber(values[2]) then n" +       //lua的数组索引从1开始 values[1] = recvCnt,values[2] = couponCnt                     "  redis.call('hincrby',KEYS[1],'recvCnt',1);n" +                     "  return true;n" +                     "elsen " +                     "  return false;n" +                     "endn"; 

4.2 redis调用lua脚本

        //执行调用         execute(GET_COUPON_CODE, keys);         //此处将数值类型转化为Long         public Long execute(String redisScript,List<String> keys){               RedisScript<Long> REDIS_SCRIPT = new DefaultRedisScript<>(redisScript, Long.class);               return redisTemplate.execute(REDIS_SCRIPT,keys);     } 

4.3 lua基本用法

  redis.call()   redis.pcall()   call与pcall基本上一样。脚本报错时,call会直接报错,pcall不会报错,会把错误信息放到lua table 的err字段中。 

五. 说明

1.记得利用“n”分行,也可以利用string的append拼接
2.values[n]对应有序数组keys,需要控制好各数据顺序
3.lua的数组索引从1开始

六. 总结

我们先分析场景,通过多种方案对比,选用了redis+lua的组合来满足我们的业务需要。利用redis单线程的特点,以及redis2.6版本后开始对lua的支持,我们采用redis执行lua脚本来确保我们查询+修改的串行执行。后面我们展示了code的实现案例,以及介绍了lua脚本的一些注意事项,可以依葫芦画瓢形式自己实现自己的需求。综合而言,我们分析场景应先分析其核心问题,然后利用一些更简洁的方法或小技巧来落地。


发表评论

分类目录

2019年十一月
« 10月    
 123
45678910
11121314151617
18192021222324
252627282930  

近期评论