Redis事务+Lua脚本

Redis的事务

使用multi开启事务,但是redis的事务只是对语法的检查,它的事务是非常弱的事务,无法解决运行时的错误,因此实际使用中不要使用redis的事务(也可以根据自己的实际业务场景选择是否使用事务,当然并不建议使用multi的事务方式)

Transaction multi = jedis.multi();
multi.set(RS_TRANS_NS+"test1","a1");
multi.set(RS_TRANS_NS+"test2","a2");
multi.set(RS_TRANS_NS+"test3","a3");
List<Object> execResult = multi.exec();

使用事务+Watch机制(compareAndSwap)

/*使用watch功能*/
String watchResult = jedis.watch(watchKeys);
if(!"OK".equals(watchResult)) {
    throw new RuntimeException("执行watch失败:"+watchResult);
}
Transaction multi = jedis.multi();
multi.set(RS_TRANS_NS+"test1","a1");
multi.set(RS_TRANS_NS+"test2","a2");
multi.set(RS_TRANS_NS+"test3","a3");
List<Object> execResult = multi.exec();
if(execResult==null){
    throw new RuntimeException("事务无法执行,监视的key被修改:"+watchKeys);
}

PipeLine+事务

Pipeline pipelined = jedis.pipelined();
pipelined.multi();//开启事务
//。。。。。等等命令
pipelined.exec();//提交事务

以上的方式都无法实现事务的原子性操作,因此引入了Lua脚本

Lua脚本

redis使用lua的好处:

  • 节省网络开销,一个lua脚本中可以包含多条redis命令,通过网络一次发送给redis,节省了网络开销
  • 原子性操作,由于redis是单线程的,一个lua脚本会被当作一条命令执行
  • 复用性,lua脚本是存储在redis中的,这意味着其他客户端可以复用这个脚本来执行相应操作。
命令说明
evalredis中提供的执行lua脚本的命令
redis.call(‘redis的指令’)用于在lua脚本中调用redis的指令,执行返回操作
script load “lua脚本”将返回一个sha1的一个字符串摘要,并且该lua脚本被换存在了redis中
evalsha “上方返回的sha1的摘要”执行redis缓存的lua脚本,可以减少lua脚本传递产生的网络开销。也可以实现lua脚本的复用性

使用场景:

  • Redis+Lua实现简单的计数器的限流

    Lua脚本rateLimiter.lua

    --java端送入三个参数(1个key,2个param  )string
    --limitKey(redi中key的值)
    local key =KEYS[1];
    --limit(次数)
    local times = ARGV[1];
    --expire(秒S)
    local expire = ARGV[2];
    --对key-value中的 value +1的操作  返回一个结果
    
    local afterval=  redis.call('incr',key);
    if afterval ==1 then --第一次
        redis.call('expire',key,tonumber(expire) )  --失效时间(1S)  TLL 1S
        return 1; --第一次不会进行限制
    end
    --不是第一次,进行判断
    if afterval > tonumber(times) then
        --限制了
        return 0;
    end
    
    return 1;
    

    服务端方法,判断是否被限流了

    @Service
    public class IsAcquire {
        @Autowired
        private Jedis jedis;
        //引入一个Redis的Lua脚本的支持
        private DefaultRedisScript<Long> getRedisScript;
    
        //判断限流方法---类似于RateLimiter
        public boolean acquire(String limitKey,int limit,int expire) throws  Exception{
            //连接Redis
            getRedisScript =new  DefaultRedisScript<>();
            getRedisScript.setResultType(Long.class);//脚本执行返回值 long
            getRedisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("rateLimiter.lua")));
            Long result = (Long)jedis.eval(getRedisScript.getScriptAsString(),
                    1,limitKey,String.valueOf(limit),String.valueOf(expire));
            if(result ==0){
                return false;
            }
            return true;
        }
    }
    

    控制层返回用户视图

    @RestController
    public class Controller {
        @Autowired
        IsAcquire isAcquire;//手下的分布式限流
        //秒杀接口
        @RequestMapping("/order")
        public String killProduct(@RequestParam(required = true) String name) throws Exception{
            //rateLimiter.tryAcquire(1); //调用
            if(isAcquire.acquire("iphone",10,60)){//60秒只能进行10次
                System.out.println("业务成功!");
                return "恭喜("+name+"),抢到iphone!";
            }else{
                System.out.println("-----------业务被限流");
                return "对不起,你被限流了!";
            }
        }
    }
    
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值