RedisTemplate executes Redis script -- source code analysis

I rarely use scripting in projects. Now that I have used it, I should understand it, improve myself, and learn it.

Reprinted from: http://www.cnblogs.com/luochengqiuse/p/4641311.html

Students who have used Redis scripts know that this is mainly used to prevent race conditions. Because scripts are executed sequentially. (Don't worry about efficiency issues) For example, I use it at work to set the highest score for the exam.


If you haven't used it yet, read the introduction of Redis script, send script, cache script, send sha1 execution script, and basic lua script syntax.


1. Usage scenarios of Redis scripts
In some cache settings, race conditions often occur, and data is incorrect due to concurrency. For example, the well-known ++ operation. If we implement ++ through Redis ourselves, it is easy to have errors under concurrency. So Redis provides the incr function. When I set the highest score, I get a score of 70, an A score of 80, and then set an 80. Then at the same time, B gets a score of 70, but B scores 78, 78>70, and then sets 78. In this case, B's modification replaces A's modification. This is also the first type of update loss in the database isolation level.


To prevent this problem, Redis provides a way to execute commands sequentially, which is to use scripts.


2. Script class RedisScript
RedisTemplate provides a high level of support for scripts, and the execution method is similar to the previous one, all of which are callbacks through connection. However, it should be noted here that the script does not support transactions, so the script cannot perform connection.multi() to start the transaction before, nor can spring be used to start the transaction with the @Transactional annotation. These will throw an exception, and you can test it yourself.


Seeing this, we create a new script class with the highest score, MaxscoreScript, which inherits from the interface RedisScript and needs to implement three methods:


public interface RedisScript<T> {


    /**
     * @return The SHA1 of the script, used for executing Redis evalsha command
     */
    String getSha1();


    /**
     * @return The script result type. Should be one of Long, Boolean, List, or deserialized value type. Can be null if
     * the script returns a throw-away status (ie "OK")
     */
    Class<T> getResultType();


    /**
     * @return The script contents
     */
    String getScriptAsString();


}
 


getSha1(), is to get Script summary. Anyone who has read the script should know that Redis supports caching scripts. After the script is sent for the first time, the sha1 summary information of the script is directly sent to execute directly, probably to save transmission costs. After all the abstract only needs 32 characters.


getResultType() is the class for obtaining the return type, which needs to be consistent with the defined generic type T. In fact, in the Basedao of Hibernate, we have used the generic class directly through the instance. Although I don't understand why it is added here, there is absolutely nothing wrong with implementing this method.


getScriptAsString(), no doubt, this is to return the script content. As for how to write the script, there is no explanation here.


 


3. Execute the script using RedisTemplate

    public <T> T execute(RedisScript<T> script, List<K> keys, Object... args) {
        return scriptExecutor.execute(script, keys, args);
    }


    public <T> T execute(RedisScript<T> script, RedisSerializer<?> argsSerializer, RedisSerializer<T> resultSerializer,
            List<K> keys, Object... args) {
        return scriptExecutor.execute(script, argsSerializer, resultSerializer, keys, args);
    }


In contrast, the second passes in the serializer itself, and the first uses the default key and value serializers.


The second one is strongly recommended. Then uniformly pass in (StringSerializer), and all the parameters inside are converted to String. When I was using it, lua made a size comparison and found that the comparison was wrong. Later, I found out that in the JdkSerializer, Redis regarded him as a string of \xu.. such a string, the string comparison is obviously inaccurate. But if it is "11", "12"


Of course, there may be other business scenarios that do not require this, and are also possible. Analyze and decide for yourself.

We see the source code for executing the script

    protected <T> T eval(RedisConnection connection, RedisScript<T> script, ReturnType returnType, int numKeys,
            byte[][] keysAndArgs, RedisSerializer<T> resultSerializer) {


        Object result;
        try {
            result = connection.evalSha(script.getSha1(), returnType, numKeys, keysAndArgs);
        } catch (Exception e) {


            if (!exceptionContainsNoScriptError(e)) {
                throw e instanceof RuntimeException ? (RuntimeException) e : new RedisSystemException(e.getMessage( ), e);
            }


            result = connection.eval(scriptBytes(script), returnType, numKeys, keysAndArgs);
        }


        if (script.getResultType() == null) {
            return null;
        }


        return deserializeResult(resultSerializer, result);
    }
 


This piece of code is finally called, and there are still some serialization parameter operations that have not been posted before.


By default, the sha1 abstract is directly submitted, and the exception is obtained. If the exception belongs to the exceptionContainsNoScriptError, the execution script is sent, and the returned result is obtained and deserialized by the user-defined result serializer.


4. Summary


This article talks about how RedisTemplate uses scripts. It may be incomprehensible to students who do not understand the use of scripts in connection or under the redis console. But if you know how to send scripts from the console, then through the use of RedisTemplate, many problems will be solved.


That's all I can bring to you here. In the next article, I will list the problems I have encountered, a lesson from the past, and a teacher for the future. I hope that everyone will not be stuck in the card for too long after they meet in the future.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325732009&siteId=291194637