Application of Lua script in Redis

Redis Lua technology, I have paid attention to it before, and I am free today. I changed the ID generator based on Redis in the project to use Lua script to prevent concurrent ID conflicts.

Benefits of using Lua in Redis

  • Reduce network overhead. Multiple requests can be sent at once via a script, reducing network latency
  • Atomic operation. Redis will execute the entire script as a whole, and will not be inserted by other commands in the middle. Therefore, there is no need to worry about race conditions in the process of writing the script, and there is no need to use transactions.
  • Reuse. The footsteps sent by the client will be permanently stored in redis, so that other clients can reuse this script without using code to complete the same logic.

Redis Lua scripts and transactions

By definition, the script in Redis is itself a transaction, so anything that can be done in a transaction can also be done in the script. And in general, using scripts is easier and faster.

When using transactions, you may encounter the following two errors:

  • Before the transaction executes EXEC , the enqueue command may be wrong. For example, the command may produce syntax errors (wrong number of parameters, parameter name error, etc.), or other more serious errors, such as insufficient memory (if the server uses maxmemoryto set the maximum memory limit of words).
  • The command may fail after the EXEC call. For example, the commands in the transaction may have handled the wrong type of key, such as using a list command on a string key, and so on.

For errors that occurred before EXEC execution, the client's previous practice was to check the return value of the command enqueue: if the command returns when the enqueue is enlisted QUEUED, the enqueue was successful; otherwise, the enqueue failed. If a command fails during enqueue, most clients will stop and cancel the transaction.

However, starting with Redis 2.6.5, the server will record the failure of the command enqueue, and when the client calls the EXEC command, it refuses to execute and automatically gives up the transaction.

Prior to Redis 2.6.5, Redis only executed those commands that successfully enqueued in the transaction, and ignored those commands that failed to enqueue. The new processing method makes it simple to include transactions in the pipeline, because sending transactions and reading transaction responses only need to communicate with the server once.

As for the errors generated after the EXEC command is executed, there is no special treatment for them: even if a certain command / some commands in the transaction generate an error during execution, other commands in the transaction will continue to be executed.

After testing, the exception handling method in Lua is the same as the redis transaction. It can be said that these two things are the same, but Lua supports caching and can reuse scripts. This is not the original office.

For more business related information, see this website

How to use lua in Redis

Using lua script in redis mainly uses three commands

  • eval
  • evalsha
  • script load
    eval is used to directly execute the lua script, the usage is as follows
EVAL script numkeys key [key ...] arg [arg ...]

> eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second
1) "key1"
2) "key2"
3) "first"
4) "second"

The key represents the rediskey
arg to be operated. The custom parameter
numkeys can be passed to determine how many
scripts the key has. It is the lua script you wrote. The lua script
uses KEYS [1] and ARGV [1] to obtain the passed key and arg

See the Lua tutorial for details

When using the eval command, you can notice that the executed script must be sent every time, so there is bound to be a certain network overhead, so redis caches the lua script, and the
script load command through script load and evalsha will be in The redis server caches your lua script and returns the SHA1 checksum of the script content, and then passes the SHA1 checksum through evalsha to find the server cached script to call. The format and usage of these two commands are as follows
SCRIPT LOAD script
EVALSHA sha1 numkeys key [key ...] arg [arg ...]

redis> SCRIPT LOAD "return 'hello moto'"
"232fd51614574cf0867b83d384a5e898cfd24e5a"

redis> EVALSHA 232fd51614574cf0867b83d384a5e898cfd24e5a 0
"hello moto"

SHA1 has the following characteristics: it is not possible to recover information from the message digest; two different messages will not produce the same message digest, (but there will be 1x10 ^ 48% chance of the same message digest, which is generally ignored when used) .

spring-data-redis operation lua

The above is how to call the lua script in the redis console. Now let's talk about how to
call redis in java. Calling redis in java generally uses jedis. For calling lua script, the package made by spring-data-redis package is used More convenient, the bottom layer is also based on jiedis, so we directly talk about how redisTemplate in spring-data-redis calls lua
to import dependencies first

<dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-redis</artifactId>
                <version>1.8.1.RELEASE</version>
</dependency>

Then we use the StringRedisTemplate class to operate

@Resource
private StringRedisTemplate stringRedisTemplate;

public <T> T runLua(String fileClasspath, Class<T> returnType, List<String> keys, Object ... values){
        DefaultRedisScript<T> redisScript =new DefaultRedisScript<>();
        redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource(fileClasspath)));
        redisScript.setResultType(returnType);
        return stringRedisTemplate.execute(redisScript,keys,values);
    }
The framework encapsulates RedisScript lua script objects, and the results may be automatically converted lua java script execution type for the configuration, and so long as to execute method is called directly
and the execute logic encapsulated evalsha optimized source code as follows
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);
    }

Because the algorithm of sha1 is universal, you can calculate the SHA1 checksum in advance in the java client, and then use evalsha to execute the script. If the script corresponding to SHA1, then use eval to execute. After eval is executed once, it can be used next time. Directly call evalsha, reducing network overhead

lua Debug

After we wrote a lua script, the data types of lua and redis are inconsistent, there is a conversion, and if you encounter a complex logic lua script, if you can't debug, just walk this logic in your own brain, it is unscientific, if Redis lua also provides debug function. It needs to be executed on the redis client.
Run lua's eval, and add -ldb to enable the debug function. debug only supports the eval command.

./redis-cli - ldb --eval /tmp/script.lua mykey somekey, arg1 arg2

For the full text, see: https://www.jianshu.com/p/366d1b4f0d13

Guess you like

Origin www.cnblogs.com/kitor/p/12757865.html