SpringBoot中Redis 采用 Lua 脚本解决多条Redis请求原子性问题

SpringBoot中Redis 采用 Lua 脚本解决多条Redis请求原子性问题,第1张

先说明下遇到的问题,我这边有两个请求,

1. key = a; num= 10

通过Redis 检查 某key的value的值,检查是否大于num,如果小于,则不扣除。如果大于则扣除 ,并返回扣除后的数值

2.key1 = a;key2 = b; num = 10

.通过Redis 对两个key的value进行检查,检查两个key的value和是否大于num,如果小于,则不扣除。如果大于,先扣除key为a的value,如果a不够扣,再扣除key为b的value。并返回json字符串,分别写入a和b所扣除的数值

这里通过Redis进行处理

环境:

JDK1.8

SprintBoot 2.2.6.RELEASE

spring-data-redis-2.2.6.RELEASE

1.第一个需求,这个是常见的预扣校验的需求。

@Resource
private RedisTemplate redisTemplate;

//LUA脚本,也可以采用外置文件的方式读取
private static final String SUBTRACT_LUA = new StringBuilder()
                .append("if (redis.call('exists', KEYS[1]) == 1) then")
                .append("    local temp = tonumber(redis.call('get', KEYS[1]));")
                .append("    local num = tonumber(ARGV[1]);")
                .append("    if (temp >= num) then")
                .append("        return redis.call('incrby', KEYS[1], 0 - num);")
                .append("    end;")
                .append("    return -2;")
                .append("end;")
                .append("return -1;").toString();

public Long subtract(String key, Long number, Long initNumber) {
        long result = subtract(key, number);
        // 初始化
        if (result == -1) {
            redisTemplate.opsForValue().setIfAbsent(key, initNumber);
            result = subtract(key, moneyNum);
        }
        //余额不足,返回null
        if (result == -2) {
            return null;
        }
        return result;
}

//实际扣除方法,成功返回扣除后数值,失败返回 -2 或 -1
private Long subtract(String key, Long num) {
        // 脚本里的KEYS参数
        List keys = new ArrayList<>();
        keys.add(key);
        DefaultRedisScript longDefaultRedisScript = new DefaultRedisScript<>();
        longDefaultRedisScript.setResultType(Long.class);
        longDefaultRedisScript.setScriptSource(new StaticScriptSource(SUBTRACT_LUA));
        Long result = redisTemplate.execute(longDefaultRedisScript, keys, num);
        return result;
}

2.第二个需求,需要返回拼接的字符串。

@Resource
private RedisTemplate redisTemplate;

public static final String SUBTRACTS_LUA = new StringBuilder()
                .append("if (redis.call('exists', KEYS[1]) == 1 and redis.call('exists', KEYS[2]) == 1) then")
                .append("    local temp1 = tonumber(redis.call('get', KEYS[1]));")
                .append("    local temp2 = tonumber(redis.call('get', KEYS[2]));")
                .append("    local num = tonumber(ARGV[1]);")
                .append("    local temp = temp1 + temp2;")
                .append("    if (temp >= num) then")
                .append("        if (temp1 >= num) then")
                .append("            redis.call('incrby', KEYS[1], 0 - num);")
                .append("           return string.format('{\"'..KEYS[1]..'\":%d}', num);")
                .append("        else")
                .append("            redis.call('set', KEYS[1], 0);")
                .append("            local num = (num - temp1);")
                .append("            redis.call('incrby', KEYS[2], 0 - num);")
                .append("            return string.format('{\"'..KEYS[1]..'\":%d, \"'..KEYS[2]..'\":%d}', temp1, num);")
                .append("        end;")
                .append("    end;")
                .append("    return '-2';")
                .append("end;")
                .append("return '-1';").toString();


public Map subtracts(String key1, String key2, Long number, Long initNumber1, Long initNumber2) {
        String result = subtracts(key1, key2, number);
        // 初始化
        if ("-1".equals(result)) {
            redisTemplate.opsForValue().setIfAbsent(key1, initNumber1);
            redisTemplate.opsForValue().setIfAbsent(key2, initNumber2);
            result = subtracts(key1, key2, number);
        }
        //数值不足,返回null
        if ("-2".equals(result)) {
            return null;
        }
        JSONObject jsonObject = JSONUtil.parseObj(result);
        Map r = new HashMap<>();
        r.put(key1, jsonObject.getLong(key1));
        r.put(key2, jsonObject.getLong(key2));
        return r;
}

private String subtracts(String key1, String key2, Long num) {
        DefaultRedisScript defaultRedisScript = new DefaultRedisScript<>();
        defaultRedisScript.setResultType(String.class);
        defaultRedisScript.setScriptSource(new StaticScriptSource(SUBTRACTS_LUA));
        return redisTemplate.execute(defaultRedisScript, redisTemplate.getValueSerializer(), redisTemplate.getStringSerializer(),
                Lists.newArrayList(key1, key2), num);
}

主要遇到坑的地方是Redis入参和返回序列化的问题。

redisTemplate.execute(defaultRedisScript, redisTemplate.getValueSerializer(), redisTemplate.getStringSerializer(),

如果对您有帮助,请点赞三连谢谢。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/725805.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-26
下一篇 2022-04-26

发表评论

登录后才能评论

评论列表(0条)

保存