Springboot uses lua script to store redis data and problems

springboot uses rua script to execute redis command record

1. Question 1: In the Lua script, if conditions are compared?

-- 如果传入的最大时间大于redis的最大时间 更新 lua脚本数组下标从1开始
local redisMaxTime = redis.call("get", KEYS[1])
-- 如果不为空,再去比较时间大小,redis存值为90,传过来的参数为690,执行完代码,redis的值未更新
if ARGV[1] > redisMaxTime then
	redis.call("set", KEYS[1], ARGV[1])
end

Phenomenon: 690> 90 The stored value of redis will be updated according to the conditions, but the result has not been updated! ! !

Process: Check the information on the Internet. The if judgment of the Lua script is correct. Changing> to ~= does not mean there is no problem, but the requirement is to update the maximum value of the corresponding key.
Looking at the information all the way to no avail, I also consulted some big guys in the spring development group, suggesting that the stored value should be int, which is a good comparison.
redisTemplate.opsForValue().set(key, 10, 3600, TimeUnit.SECONDS);
The value corresponding to the key is set to 10, 3600s expires.

But the if condition is still wrong, the value of redis is still not updated to the maximum value. I watched it on the Internet for a long time, and it almost collapsed. I have been thinking about why 690> 90 is not true. 0.0
finally asked the driver of Xia Tang, fuck him Let me try the value tonumber() in it, it turned out to be right, shit, let's see the reason: the value type taken out from redis is
object, shit! ! I saved the int type, and I thought it was also an int when I took it out, so careless! ! Hey, I've been pitted by this for a long time, tears collapsed. It seems that a lot of details are still not handled properly. I
thought that the stored int was also an int when it was taken out. I neglected~~
Solution: if tonumber(ARGV[1])> tonumber(redisMaxTime) then

2. Question 2: Redis cluster fails in the following ways! Prompt that the cluster does not support... (My own machine is in stand-alone mode, the test passed, but it can't be used in a cluster environment...)

public void updateMaxMinTime(String maxTimeKey, String minTimeKey, Integer consumeTime) {
    
    
        // 执行 lua 脚本
        DefaultRedisScript<Object> redisScript = new DefaultRedisScript<>();
        // 指定 lua 脚本
        // 先获取指定key的值,然后和传入的arg比较是否相等,相等值删除key,否则直接返回0。
        redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("script/updateMaxMinTime.lua")));
        // 指定返回类型
        redisScript.setResultType(Object.class);
        List<String> keyList = new ArrayList<>();
        keyList.add(maxTimeKey);
        keyList.add(minTimeKey);
        // 参数一:redisScript,参数二:key列表,参数三:arg(可多个)
        redisTemplate.execute(redisScript, keyList, consumeTime);
        log.info("执行updateMaxTime.lua脚本结束.");
    }
解决方案:查阅资料,使用以下方式。
public void updateMaxMinTime1(String maxTimeKey, String minTimeKey, Integer consumeTime) {
    
    
        List<String> keys = new ArrayList<>();
        keys.add(maxTimeKey);
        keys.add(minTimeKey);
        List<String> args = new ArrayList<>();
        args.add(consumeTime.toString());
        //spring自带的执行脚本方法中,集群模式直接抛出不支持执行脚本异常,此处拿到原redis的connection执行脚本
        Object result = redisTemplate.execute(new RedisCallback<Object>() {
    
    
            @Override
            public Object doInRedis(RedisConnection connection) throws DataAccessException {
    
    
                Object nativeConnection = connection.getNativeConnection();
                // 集群模式和单机模式虽然执行脚本的方法一样,但是没有共同的接口,所以只能分开执行
                // 集群模式
                if (nativeConnection instanceof JedisCluster) {
    
    
                    return ((JedisCluster) nativeConnection).eval(LUA, keys, args);
                }
                // 单机模式
                else if (nativeConnection instanceof Jedis) {
    
    
                    return ((Jedis) nativeConnection).eval(LUA, keys, args);
                }
                return null;
            }
        });
        log.info("执行updateMaxTime.lua脚本结束.{}", result);
    }
**但是问题又来了,redis.clients.jedis.exceptions.JedisClusterException: 
			No way to dispatch this command to Redis Cluster because keys have different slots.

I read on the Internet and some people say that jedisCluster does not support cross-slot operations such as mget/mset and
needs to change the redis driver to lettuce ~~**


<!-- jedis客户端 -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>


		<!--解决 No way to dispatch this command to Redis Cluster because keys have different slots 使用lettuce-->
        <dependency>
            <groupId>io.lettuce</groupId>
            <artifactId>lettuce-core</artifactId>
            <version>5.1.8.RELEASE</version>
        </dependency>

Will use lettuce to replace the jedis client... But the problem is coming again, CROSSSLOT Keys in request don't hash to the same slot

This error will be reported: CROSSSLOT Keys in request don't hash to the same slot-keys cannot fall on the same node
. Two keys are passed in, key1 and key2, but they are not in the same slot when counting slots. Inside a slot.
For prompts, let the keys fall in the same slot.
Use hash tags (a hash tag is a mark for the beginning and end of a part of the hash string), that is, if the structure of the key is {XXX}key,
it will only If you partition XXX in {}, it will fall into the same slot.

Modification scheme: When adding key, add {xxx}
List keys = new ArrayList<>();
keys.add("{STATISTICS}" + maxTimeKey);
keys.add("{STATISTICS}" + minTimeKey);
final execution : Redis is stored successfully! ! !

3. Question 3: If you use lettuce to link redis, it will appear: lettuce will have a problem in docker, if there is no connection to keep alive, redis will not be used after 240s

what? ? ? Colleagues said that there will be this risk, let's try: cluster mode Redis supports pipeline
time because of time, I didn't try pipline.
Delete the lettuce dependency, or use jedis or use eval to execute the lua script

public void updateMaxMinTime(String maxTimeKey, String minTimeKey, String successKey, String failKey, Integer consumeTime) {
    
    
        try {
    
    
            // 设置key列表
            List<String> keys = new ArrayList<>();
            keys.add(applicationName + SEPARATOR + STATISTICS + maxTimeKey);
            keys.add(applicationName + SEPARATOR + STATISTICS + minTimeKey);
            keys.add(applicationName + SEPARATOR + STATISTICS + successKey);
            keys.add(applicationName + SEPARATOR + STATISTICS + failKey);
            // 设置参数列表
            List<String> args = new ArrayList<>();
            args.add(consumeTime.toString());
            redisTemplate.execute(new RedisCallback<Object>() {
    
    
                @Override
                public Object doInRedis(RedisConnection connection) throws DataAccessException {
    
    
                    Object nativeConnection = connection.getNativeConnection();
                    // 集群模式和单机模式虽然执行脚本的方法一样,但是没有共同的接口,所以只能分开执行
                    // 集群模式
                    if (nativeConnection instanceof JedisCluster) {
    
    
                        return ((JedisCluster) nativeConnection).eval(lua_script, keys, args);
                    }
                    // 单机模式
                    else if (nativeConnection instanceof Jedis) {
    
    
                        return ((Jedis) nativeConnection).eval(lua_script, keys, args);
                    }
                    return null;
                }
            });
            log.info("执行updateMaxTime.lua脚本结束。");
        } catch (Exception e) {
    
    
            log.error("执行updateMaxTime.lua脚本失败。");
        }
    }
脚本:
-- 如果传入的最大时间大于redis的最大时间 更新 lua脚本数组下标从1开始
	local redisMaxTime = redis.call("get", KEYS[1])
	-- 如果不为空,再去比较时间大小
	if redisMaxTime then
		if tonumber(ARGV[1]) > tonumber(redisMaxTime)
		then
			redis.call("set", KEYS[1], ARGV[1])
		end
	-- 如果为空,则说明是第一次存储,直接存入redis
	else
		redis.call("set", KEYS[1], ARGV[1])
	end

	-- 如果传入的最小时间小于redis的最小时间 更新
	local redisMinTime = redis.call("get", KEYS[2])
	if redisMinTime then
		if tonumber(ARGV[1]) < tonumber(redisMinTime)
		then
			redis.call("set", KEYS[2], ARGV[1])
		end
	else
		redis.call("set", KEYS[2], ARGV[1])
	end

	-- 成功次数key是否对应值,没有则设置为0
	if not redis.call("get", KEYS[3]) then
		redis.call("set", KEYS[3], 0)
	end

	-- 失败次数key是否对应值,没有则设置为0
	if not redis.call("get", KEYS[4]) then
	   redis.call("set", KEYS[4], 0)
	end

Guess you like

Origin blog.csdn.net/qq_33371766/article/details/109730158