在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
前言我们都知道redis是高性能高并发系统必不可少的kv中间件,它以高性能,高并发著称,我们常常用它做缓存,将热点数据或者是万年不变的数据缓存到redis中,查询的时候直接查询redis,减轻db的压力,分布式系统中我们也会拿它来做分布式锁,分布式id,幂等来解决一些分布式问题,redis也支持lua脚本,而且能够保证lua脚本执行过程中原子性,这就使得它的应用场景很多,也很典型,在redisson这个redis客户端中,它的各种分布式锁底层就是使用lua来实现的。本文主要是学习一下redis lua脚本的编写,以及在redisson这个redis客户端中是怎样使用的,实战一下秒杀场景redis减库存lua脚本的编写,并伪真实环境压测查看效果。 1.redisson介绍redisson是一个redis的客户端,它拥有丰富的功能,而且支持redis的各种模式,什么单机,集群,哨兵的都支持,各种各样的分布式锁实现,什么分布式重入锁,红锁,读写锁,信号量的,然后它操作redis的常用数据结构就跟操作jdk的各种集合一样简单。 maven依赖 <dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>3.8.1</version> </dependency> 创建RedissonClient对象 Config config = new Config(); config.useSingleServer() .setAddress("redis://xxx:xx"); RedissonClient redissonClient = Redisson.create(config); 它的功能超级多,下面只是列举了一些常用的,还有什么Bloom过滤器,队列等等。 // string redissonClient.getBucket("name").set("zhangsan"); redissonClient.getBucket("name").get(); // hash RMap<Object, Object> user = redissonClient.getMap("user"); user.put("name","zhangsan"); user.put("age",11); // list RList<String> names = redissonClient.getList("names"); names.add("zhangsan"); names.add("lisi"); names.add("wangwu"); // set RSet<Object> nameSet = redissonClient.getSet("names"); nameSet.add("lisi"); nameSet.add("lisi"); //lock RLock lock = redissonClient.getLock("lock"); lock.lock(); lock.unlock(); // 分布式id RAtomicLong id = redissonClient.getAtomicLong("id"); long l = id.incrementAndGet(); 2. redis lua脚本编写与执行其实redis中的lua脚本并不难,你也不需要把lua语言再去重学一遍,全凭感觉就好了,使用的时候去查下语法就ok了。 return redis.call('set','name','zhangsan'); 其实就是跟redis交互命令一个样子,再使用lua语言做一些条件分支,循环啥的,就完成了一些稍微复杂的逻辑。 多说无益,这里直接使用redisson客户端实践一下。 public class RedisLua { private static final Config config ; private static final RedissonClient redisson ; static { config = new Config(); config.useSingleServer() .setAddress("redis://ip:port"); redisson = Redisson.create(config); } public static void main(String[] args) throws InterruptedException { redisson.getBucket("name").set(11); RScript script = redisson.getScript(); String result = script.eval(RScript.Mode.READ_ONLY, new StringCodec(),"return redis.call('get','name');", RScript.ReturnType.VALUE); System.out.println(result); } } 可以看到就是使用getScript方法获取一个script对象,然后调用script 对象的eval方法,这个script对象其实还是用好多个方法的,evalSha等等,可以自己研究下。然后就是通过lua脚本获取我上面set进去的name值。 RScript script = redisson.getScript(); List<Object> keys=new ArrayList<>(); keys.add("age"); String re = script.eval( RScript.Mode.READ_WRITE, new StringCodec(), "return redis.call('set',KEYS[1],ARGV[1]);", RScript.ReturnType.VALUE, keys,1); Object age = redisson.getBucket("age").get(); System.out.println(age); KEYS[1] 就对应这age, ARGV[1]就对应1,同理,KEYS[2] 就对应keys集合中的第二个元素。 3.redis减库存lua脚本先介绍下下单减库存是怎么干的吧,其实一般库存有可用库存与预占库存,再下单的时候,就将可用库存减去你购买的商品数量看看是否是小于0,如果是小于0的话,说明库存不够了,就不让下单购买了,如果可用库存充足,可用库存减去购买商品数量,预占库存加上你购买商品数量,当用户超时未支付或者是手动取消订单的时候,就会去预占库减去用户购买商品数,可用库存加上商品数,其实还有一个已售库存,商家发货,已售库存加上商品数,预占减去商品数,大体上是这个逻辑。 public static final String LOCK_STOCK_LUA= "local counter = redis.call('hget',KEYS[1],ARGV[1]); \n" + "local result = counter - ARGV[2];" + "if(result>=0 ) then \n" + " redis.call('hset',KEYS[1],ARGV[1],result);\n" + " redis.call('hincrby',KEYS[1],ARGV[3],ARGV[2]);\n" + " return 1;\n" + "end;\n" + "return 0;\n"; 我这里已经写好了,直接贴出来。 local counter = redis.call('hget',KEYS[1],ARGV[1]); 获取可售库存数量 local result = counter - ARGV[2]; if(result>=0 ) then 可售库存减去要购买的商品数量,如果是大于0的话,说明库存还够。 redis.call('hset',KEYS[1],ARGV[1],result); redis.call('hincrby',KEYS[1],ARGV[3],ARGV[2]); return 1; 重新设置可售库存数量,增加预占库存,然后返回1 4.实战4.1 减库存逻辑减库存逻辑其实就是先是用lua脚本减redis库存,如果成功再去减数据库中的真实库存,如果减redis库存失败,库存不足,就不会再走后面减真实库存的逻辑了。 4.2 压测我们这个实战是在阿里云进行的 redis监控,可以看到,这点并发对redis来说就是毛毛雨。cpu才使用7% 云服务器这块手速有点慢,没截图出来,cpu跟内存都在50%左右。 mysql数据库,可以看到cpu飙上去了,内存飙上去了。 数据库数据: 可以发现并没有出现超卖现象。 到此这篇关于redis lua脚本实战秒杀扣减库存的实现的文章就介绍到这了,更多相关redis lua战秒杀扣减库存内容请搜索极客世界以前的文章或继续浏览下面的相关文章希望大家以后多多支持极客世界! |
请发表评论