1. Introduction
Redis is a high-performance KV memory database. Besides the basic role of cache middleware, Redis also has many uses, such as Redis GEO geo-information calculation shared by Fat Man before. Redis provides a rich set of commands that we can use to perform some calculations. Individual Redis commands are atomic, and sometimes we want to be able to combine multiple Redis commands and have them execute atomically and even reuse them. Redis developers realized that this scenario was still common, and in version 2.6 introduced a feature to address this issue: Redis executes Lua scripts.
2. Lua
Lua is one of the oldest languages in the world of Warcraft, and it should be familiar to anyone who plays it. The add-ons for WOW were scripted in Lua. Lua is widely used in highly concurrent online games.
Lua is widely used as an embedded script in other languages, especially C/C++. The syntax is simple and small, and the source code is just over 200 K, which is probably why Redis officially chose it.
Another star software, Nginx, also supports Lua, and you can do a lot of useful things with Lua.
3. Lua is not difficult
The official Redis guidelines also point out not to write overly complex logic in Lua scripts.
Learning a language in order to implement a feature can seem daunting. Lua isn’t hard to learn, and for the purposes of this article we don’t need to learn the full features of Lua, but use Lua lightweight in Redis. This is not difficult for someone who has mastered a heavyweight language like Java. The basic syntax involved in Redis is described here.
Lua’s simple syntax
I personally recommend using only the following types in Redis scripts:
nil
空boolean
Boolean valuenumber
digitalstring
stringtable
表
Declare the type
Declaring a type is very simple and does not carry a type.
-- A global variable
name = 'felord.cn'
-- Local variables
local age = 18
Copy the code
Redis scripts should not use global variables in practice, local variables are more efficient.
The table type
While the first four are easy to understand, the fifth table needs to be explained briefly. It is both an array and similar to a Java HashMap (dictionary), and it is the only data structure in Lua.
Arrays are not specific types, as shown below
Lua 5.1.5 Copyright (C) 1994-2012 Lua.org, puc-rio > arr_table = {Lua 5.1.5 Copyright (C) 1994-2012 Lua.org, puc-rio > arr_table = {'felord.cn'.'Felordcn', 1} >print(arr_table[1])
felord.cn
> print(arr_table[3])
1
> print(#arr_table)
3
Copy the code
As a dictionary:
Lua 5.1.5 Copyright (C) 1994-2012 Lua.org, PUC-Rio
> arr_table = {name = 'felord.cn', age = 18}
> print(arr_table['name'])
felord.cn
> print(arr_table.name)
felord.cn
> print(arr_table[1])
nil
> print(arr_table['age'18]) >print(#arr_table)
0
Copy the code
Mixed mode:
Lua 5.1.5 Copyright (C) 1994-2012 Lua.org, puc-rio > arr_table = {Lua 5.1.5 Copyright (C) 1994-2012 Lua.org, puc-rio > arr_table = {'felord.cn'.'Felordcn',1,age = 18,nil}
> print(arr_table[1])
felord.cn
> print(arr_table[4])
nil
> print(arr_table['age'18]) >print(#arr_table)
3
Copy the code
❗ # The length of the table is not accurate, use with caution. Also avoid using mixed-mode tables in Redis scripts, and elements should avoid containing nil. In the case of uncertain elements you should use a loop to calculate the true length.
judge
The judgment is very simple. The format is:
local a = 10
if a < 10 then
print('a less than 10')
elseif a < 20 then
print('A is less than 20, greater than or equal to 10'.)
else
print(A is greater than or equal to 20 prime.)
end
Copy the code
An array of circulation
local arr = {1.2,name='felord.cn'}
for i, v in ipairs(arr) do
print('i = '..i)
print('v = '. v)end
print('-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --')
for i, v in pairs(arr) do
print('p i = '..i)
print('p v = '. v)end
Copy the code
Print result:
i = 1
v = 1
i = 2
v = 2
-----------------------
p i = 1
p v = 1
p i = 2
p v = 2
p i = name
p v = felord.cn
Copy the code
The return value
Like Python, Lua can return multiple return values. However, it is not recommended to use this feature in The Redis Lua script. If you need this feature, please wrap it in an array structure. The return value rules for supporting scripts in Spring Data Redis can be analyzed here:
public static ReturnType fromJavaType(@NullableClass<? > javaType) {
if (javaType == null) {
return ReturnType.STATUS;
}
if (javaType.isAssignableFrom(List.class)) {
return ReturnType.MULTI;
}
if (javaType.isAssignableFrom(Boolean.class)) {
return ReturnType.BOOLEAN;
}
if (javaType.isAssignableFrom(Long.class)) {
return ReturnType.INTEGER;
}
return ReturnType.VALUE;
}
Copy the code
In practice, Fat brother will use List, Boolean and Long to avoid unitary errors.
Other functions, coroutines, and other features should not appear in the Redis Lua script. Search for the built-in functions.
When you approach a new technology, you need to use it properly. If you want to play with it, it will mean higher learning costs.
Lua in Redis
Now it’s time to put the Redis Lua script into action.
The EVAL command
Redis uses the EVAL command to directly execute the specified Lua script.
EVAL luascript numkeys key [key ...] arg [arg ...]Copy the code
EVAL
Keyword of the command.luascript
The Lua script.numkeys
The number of keys that the specified Lua script needs to handle, which iskey
The length of the array.key
Zero to multiple keys, separated by Spaces, passed to the Lua scriptKEYS[INDEX]
To get the corresponding value, where1 <= INDEX <= numkeys
.arg
Is zero to multiple additional arguments, separated by Spaces, passed to the script in Lua scriptsARGV[INDEX]
To get the corresponding value, where1 <= INDEX <= numkeys
.
I’ll show you a simple script for getting the hello key:
127.0.0.1:6379 >setHello world OK 127.0.0.1:6379> get hello"world"127.0.0.1:6379 > EVAL"return redis.call('GET',KEYS[1])" 1 hello
"world"127.0.0.1:6379 > EVAL"return redis.call('GET','hello')"
(error) ERR wrong number of arguments for 'eval' command127.0.0.1:6379 > EVAL"return redis.call('GET','hello')" 0
"world"
Copy the code
From the above demo code, KEYS[1] can be directly replaced with hello, but the Redis official documentation indicates that this is not recommended. The purpose is that the command is analyzed before execution to ensure that the Redis Cluster can forward the command to the appropriate Cluster node.
Numkeys is a required command argument in any case.
Call function and pcall function
In the example above we executed a SET command with redis.call(), but we could have used redis.pcall() instead. The only difference between them is the way they handle errors. The former returns an error directly to the caller when executing a command error. The latter will wrap the error as a table we talked about above:
127.0.0.1:6379 > EVAL"return redis.call('no_command')" 0
(error) ERR Error running script (call to f_1e6efd00ab50dd564a9f13e5775e27b966c2141e): @user_script:1: @user_script: 1: Unknown Redis commandCalled from Lua script 127.0.0.1:6379> EVAL"return redis.pcall('no_command')" 0
(error) @user_script: 1: Unknown Redis command called from Lua script
Copy the code
This is like Java, which throws an exception; The latter handles the exception as a JSON return.
value
Since there are two different running environments in Redis and Lua, the corresponding transformation operation will inevitably occur when Redis and Lua pass data to each other. This transformation operation cannot be ignored in practice. For example, if a Lua script returns decimals to Redis, the decimals precision will be lost; It is safe if converted to a string.
127.0.0.1:6379 > EVAL"The return of 3.14" 0
(integer) 3 127.0.0.1:6379 > EVAL"Return tostring (3.14)" 0
"3.14"
Copy the code
It is safe to pass strings and integers according to the fat brother’s experience, and you need to check the official documents carefully and verify the rest.
Atomic execution
Lua scripts are executed atom by atom in Redis. When the Redis server executes the EVAL command, only the logic contained in the Lua script specified by the current command will be executed before the command is executed and the result is returned to the caller. All other commands sent by the client will be blocked until EVAL has been executed. Therefore, it is not advisable to write LUA scripts with too complex logic. You must ensure that LUA scripts are as efficient as possible, otherwise other clients will be affected.
Script management
SCRIPT LOAD
The script is loaded into the cache for reuse to avoid wasting bandwidth for multiple loads. Each script returns a unique string identifier through SHA verification. The cached script needs to be executed with the EVALSHA command.
127.0.0.1:6379 > SCRIPT LOAD"return 'hello'"
"1b936e3fe509bcbc9cd0664897bbe8fd0cac101b"127.0.0.1:6379 > EVALSHA 1 b936e3fe509bcbc9cd0664897bbe8fd0cac101b 0"hello"
Copy the code
SCRIPT FLUSH
Since there is a cache, there is a clear cache, but unfortunately the script cache is not removed according to SHA, but all script caches are cleared, so in production this command is generally not used during production.
SCRIPT EXISTS
Check whether one or more caches exist with an SHA identifier.
127.0.0.1:6379 > SCRIPT EXISTS b936e3fe509bcbc9cd0664897bbe8fd0cac101b 1 b936e3fe509bcbc9cd0664897bbe8fd0cac1012 (1)integer1) (2)integer) 0
Copy the code
SCRIPT KILL
Terminates the script that is executing. However, for the sake of data integrity this command does not guarantee successful termination. This command does not work if a script needs to be terminated when it executes part of the written logic. You need to execute SHUTDOWN nosave to terminate the server without persisting the data to complete the termination script.
Some other points
Understanding the above knowledge can basically meet the development of some simple Lua scripts. But there are some important points in actual development.
- Make sure that Lua scripts are fully tested to ensure the robustness of their logic, and when Lua scripts encounter exceptions, logic that has already been executed will not be rolled back.
- Try not to use the random functions provided by Lua. See the official documentation.
- Do not write in Lua scripts
function
Function, the entire script as the body of a function. - All variables declared in scripting are used
local
The keyword. - Using Lua scripts in a cluster ensures that all of the
key
In the same machine, that is, in the same slot, can be usedRedis Hash TagTechnology. - Again, Lua scripts must not contain too time-consuming, too complex logic.
5. To summarize
This article explains and demonstrates the scenarios of The Redis Lua script and the Lua programming syntax required by the Redis Lua script in detail. It also shares some points that need to be paid attention to in the actual development of the Redis Lua script. Hope to help you master this technology. That’s it for today, next time I’ll be sharing how to use Lua scripts in real Redis development, so make sure you get the hang of this one. More attention: code farmer little fat brother to get more programming knowledge dry goods.
Follow the official account Felordcn for more information
Personal blog: https://felord.cn