lua脚本的简单使用!!!

本文介绍了如何使用Lua脚本在Redis中实现单个和多个商品的库存扣减,强调了原子性和并发控制,同时展示了如何借助分布式锁确保操作的有序执行。
摘要由CSDN通过智能技术生成

一、使用lua脚本扣减单个商品的库存

 /*
    一次扣减一个库存
    使用lua脚本,保证查询和扣减商品数量执行的原子性
    1.查询库存数量
    2.判断库存数量是否大于等于实际购买数量
    3.如果大于等于,则扣减库存,返回-1
    4.如果小于,则返回库存数量
    */

@Test
    void test2() {
        StringBuilder sb = new StringBuilder();
        sb.append(" local key = KEYS[1] "); // 你要扣减的key :product.1
        sb.append(" local qty = ARGV[1] "); // 你要扣减的数量,需求量
        sb.append(" local redis_qty = redis.call('get',key) "); //获取redis库存实际数量
        sb.append(" if tonumber(redis_qty) >= tonumber(qty) then ");//需求量与库存量对比
        sb.append("     redis.call('decrby',key,qty) "); //满足
        sb.append("     return -1  "); //满足 同意返回-1
        sb.append(" else ");
        sb.append("   return tonumber(redis_qty) ");  //不满足,就给剩余库存量 ,0 1 2 。。。。
        sb.append(" end ");
        RedisScript<Long> luaScript = RedisScript.of(sb.toString(), Long.class);
        //创建一个线程池
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        for (int i = 1; i <= 5; i++) {
            executorService.execute(() -> {
                //生成随机扣减数量
                int qty = RandomUtil.randomInt(1, 6);
                Long count = stringRedisTemplate.execute(luaScript, CollUtil.newArrayList("product.5"), String.valueOf(qty));
                if (count == -1L) {
                    System.out.println(Thread.currentThread().getId() + " 扣减成功,扣减了-> " + qty);
                } else {
                    System.out.println(Thread.currentThread().getId() + "扣减失败,需求量是:" + qty + ",剩余库存量:" + count);
                }
            });
        }
        ThreadUtil.safeSleep(3000);
    }

二、使用lua脚本扣减多个商品的库存量

  /*
        扣减多个商品
        1.创建一个空的table数组用于存储库存不满足的商品的key
        2.遍历所有的商品,判断库存数量是否大于等于实际购买数量,将不满足的key放入table中
        3.判断table中是否有数据,如果有就将table返回,不执行扣减
        4.如果table没有数据,再统一遍历进行扣减所有的商品
     *

    @Test
    void test3() {
        StringBuilder sb = new StringBuilder();
        sb.append(" local table = {}  "); // 你要扣减的key :product.1
        sb.append(" local values =  redis.call('mget',  unpack(KEYS) )"); //获取多个key将其分割开来 [product.1,product.2]   =>  product.1 product.2
        sb.append(" for i = 1, #KEYS   do  ");
        sb.append("   if  tonumber(ARGV[i]) > tonumber(values[i])   then ");//如果实际出库量 > 真实库存量
        sb.append("     table[#table + 1] =  KEYS[i] .. '=' .. values[i] "); //将库存量不够商品的剩余库存量加入table中返回product.1=23
        sb.append("   end ");
        sb.append(" end ");
        sb.append(" if #table > 0 then ");//table的长度 > 0 证明有商品的库存小于出库数量
        sb.append("   return table  "); //将其返回出去
        sb.append(" end ");
        sb.append(" for i = 1 , #KEYS do ");// 所有的商品库存量都大于出库数量
        sb.append("   redis.call('decrby',KEYS[i],ARGV[i])  ");//执行扣减
        sb.append(" end ");
        sb.append(" return {} ");
        RedisScript<List> luaScript = RedisScript.of(sb.toString(), List.class);
        //定义一个集合,用来存放扣减商品所包含的信息
        List<StockProducts> stockProducts = new ArrayList();
        stockProducts.add(new StockProducts(5, 1));
        stockProducts.add(new StockProducts(4, 2));
        //获取集合中的所有key和qty数量
        List<String> keys = stockProducts.stream().map(it -> "product." + it.getId()).collect(Collectors.toList());
        Object[] qtys = stockProducts.stream().map(it -> it.getQty() + "").toArray();
        //执行lua脚本
        List<String> list = stringRedisTemplate.execute(luaScript, keys, qtys);
        if (list.isEmpty()){
            System.out.println("冻结库存");
        }else {
            for (String key_qty : list) {
                String[] split = key_qty.split("=");
                System.out.println(split[0]+"库存不足,剩余库存量:"+split[1]);
            }
        }
        ThreadUtil.safeSleep(3000);
    }

三、通过分布式锁,扣减商品的库存

package com.by;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.unit.DataUnit;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.RandomUtil;
import com.by.model.Student;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;

import javax.annotation.Resource;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


@SpringBootTest
class SetNXTests {

    @Resource(name = "redisTemplate")
    ValueOperations valueOperations;
    @Autowired
    StringRedisTemplate stringRedisTemplate;


    @Test
    void test() {
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        for (int i = 1; i <= 5; i++) {
            executorService.execute(()->{
                //某一个工人
                String ioId = "IO"+ RandomUtil.randomInt(1,1000);
                while (true){
                    Boolean b  = valueOperations.setIfAbsent("lock.product.1", ioId +" : "+ DateUtil.now());
                    if(b){
                        System.out.println(Thread.currentThread().getId() + "获取到了分布式锁-------");
                        //执行业务
                        ThreadUtil.safeSleep(3000);
                        //执行业务成功之后
                        stringRedisTemplate.delete("lock.product.1");
                        System.out.println(Thread.currentThread().getId() + "释放了分布式锁---------");
                        break;
                    } else {
                        System.out.println(Thread.currentThread().getId() + "没有获取到分布式锁");
                        ThreadUtil.safeSleep(1000);
                    }
                }

            });
        }
        ThreadUtil.safeSleep(100000);
    }


}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值