木有lua的Redis是莫得灵魂的

125 阅读1分钟

记录一下Lua脚本原子性操作Redis

命令示例

EVAL "local result = redis.call('GET', KEYS[1] ) if result == false then redis.call('SET', KEYS[1] , 'ok') redis.call('EXPIRE', KEYS[1], ARGV[1]) return 1 else return 0 end" 1 lock 60

参数解析

  • 1 : 偏移量 代表从其后开始 参数列表多少个是KEYS的数据,剩下的均为ARGV的数据
  • KEYS : 通常用来 传键
  • ARGV : 通常用来 传值

Java示例

    // 莫慌注入的实际上是一个工厂对象 熟悉的 ThreadLocal 套路 
    @Resource 
    HttpServletRequest httpServletRequest;

    @Resource 
    RedisTemplate redisTemplate;

    @Around("@annotation(limit)")
    public Object limit(ProceedingJoinPoint point, Limit limit) throws Throwable {
        User user = getUser(httpServletRequest); 
        // lua 保证查询与赋值逻辑原子性 防止插队 
        String key = "limit:" + getMd5(point.getSignature().toString()) + ":" + user.getUserId();
        String luaScript = "local result = redis.call('GET', KEYS[1] ) if result == false then redis.call('SET', KEYS[1] , 1) redis.call('EXPIRE', KEYS[1], ARGV[1]) return 1 else return 0 end";
        RedisScript<Long > redisScript = new DefaultRedisScript<>(luaScript, Long.class);
        Long result = redisTemplate.execute(redisScript, Collections.singletonList(key), limitSubmit.time());
        if (Objects.equals(result, 0L)) {
            throw new RuntimeException("OGM 慢点,慢点 要遭不住了!!!");
        }
        return point.proceed();
    }

lua 逻辑 有则返回 0 不存在 则保存 并 设置过期时间 将查询 和 保存 通过 lua 原子化 注意 lua 返回的数字要用 long 接收哦 另外 lua 只能保证批量执行 没人插队 报错了可回滚不了哦