Reference resources:
Redis has supported Lua scripting since version 2.6.0. By embedding the Lua environment in the server, Redis clients can use Lua scripts to execute multiple Redis commands atomically directly on the server side.
1. Benefits of using Lua scripts:
- Reduce network overhead: You can script multiple requests at once to reduce network latency.
- Atomic operations: redis executes the entire script as a whole, without being inserted by other commands.Therefore, you don't have to worry about race conditions or use transactions during scripting.
- Reuse: A script sent by a client persists in redis so that other clients can reuse the script without having to use code to complete the same logic.
2. Related commands:
- EVAL: Execute Lua script
- EVALSHA: Execute the Lua script according to the given sha1 check code
- SCRIPT DEBUG: Use EVAL to start debugging scripts (version 3.2.0)
- SCRIPT EXISTS: See if the specified script has been saved in the cache
- SCRIPT FLUSH: Remove all scripts from the script cache
- SCRIPT KILL: Kill the currently running Lua script
- SCRIPT LOAD: Add the script to the script cache, but do not execute it immediately
1. EVAL
EVAL script numkeys key [key ...] arg [arg ...]
- Script: The parameter is a Lua 5.1 script program.A script does not have to (and should not) be defined as a Lua function
- Number: Specifies the number of key name parameters
- Key: Represents the Redis keys (keys) used in scripting. These key name parameters can be accessed in Lua through a global variable KEYS array with subscripts starting from 1, such as KEYS[1], KEYS[2]
- arg: parameter, accessed in Lua through the global variable ARGV array, subscript from 1, such as: ARGV[1], ARGV[2]
Example:
(1) Use KEYS arrays, ARGV arrays
redis> eval "return {KEYS[1], KEYS[2], ARGV[1], ARGV[2]}" 2 key1 key2 arg1 arg2 1) "key1" 2) "key2" 3) "arg1" 4) "arg2"
(2) All elements in the output list
redis> rpush nums 1 2 3 (integer) 3 redis> eval "return redis.call('lrange', KEYS[1], 0, -1)" 1 nums 1) "1" 2) "2" 3) "3"
(3) Collection weighting
Using the lua script file method, execute from the command line, but not in redis, as follows:
redis-cli --eval lua_file key1 key2 , arg1 arg2 arg3
- Reference resources: https://www.cnblogs.com/tinywan/p/9643022.html
Example:
- Insert Test Data
redis> sadd nums 1 2 3 (integer) 3 redis> smembers nums 1) "1" 2) "2" 3) "3"
- member.lua script
-- key local key = KEYS[1] -- All parameters local args = ARGV -- Initialization result local result = {} -- Determine whether an element exists for m, n in ipairs(args) do local is_repeat = redis.call("sismember", key, n) if (is_repeat) then table.insert(result, 1, n) end end return result
- test
$ redis-cli --eval ./member.lua nums , 1 2 1) "2" 2) "1"
2. SCRIPT LOAD, EVALSHA
-
SCRIPT LOAD script: Add script to the script cache, but do not execute it immediately
- Script:lua script
-
EVALSHA sha1 numkeys key [key...] Arg [arg...]: Execute Lua script based on a given sha1 check code
- sha1: sha1 check code generated by SCRIPT LOAD
- Other parameters are the same as the EVAL command
Example:
redis> script load "return 'hello redis'" "69dd69fc0ba1e25d8e2972008b6baee8eccf7da6" redis> evalsha 69dd69fc0ba1e25d8e2972008b6baee8eccf7da6 0 "hello redis"
3. SCRIPT EXISTS, SCRIPT FLUSH
-
SCRIPT EXISTS script [script...]: Check to see if the specified script has been saved in the cache
- script: sha1 check code generated from SCRIPT LOAD
- SCRIPT FLUSH: Remove all scripts from the script cache
Example:
redis> script load "return 'hello redis'" "69dd69fc0ba1e25d8e2972008b6baee8eccf7da6" redis> script exists 69dd69fc0ba1e25d8e2972008b6baee8eccf7da6 1) (integer) 1 redis> script flush OK redis> script exists 69dd69fc0ba1e25d8e2972008b6baee8eccf7da6 1) (integer) 0
IV. SCRIPT KILL
SCRIPT KILL: Kill the currently running Lua script and take effect only if it has not performed any write operations
Note:
- This command is primarily used to terminate scripts that take too long to run, such as a script that loops indefinitely due to a BUG.
- After SCRIPT KILL executes, the currently running script is killed, and the client executing the script exits from the block of the EVAL command and receives an error as a return value.
Example:
Client A:
# There is no script currently executing redisA> script kill (error) NOTBUSY No scripts in execution right now. # Client B executes the script, Client A kills the running script redis> script kill OK (0.92s)
Client B:
# Script Dead Loop redisB> eval "local a = 1; while 1 do a=1; end" 0 # When killed, return the following information (error) ERR Error running script (call to f_c8600a7388e4dd63490c59ede33602dbef5971eb): @user_script:1: Script killed by user with SCRIPT KILL... (5.00s)
5. SCRIPT DEBUG
SCRIPT DEBUG YES|SYNC|NO: Use EVAL to start debugging scripts
- YES: Open non-blocking asynchronous debugging mode, debug Lua scripts (fallback modified data)
- SYNC: Turn on blocking synchronous debugging mode, debug Lua scripts (keep modified data)
- NO: Turn off script debugging mode
Note: LDB can be set in two modes: synchronous and asynchronous.
- In asynchronous mode, the server creates a new debug connection, does not block other connections, and rolls back all data modifications after the debug connection ends, which ensures that the initial state remains the same when debugging again.
- In synchronous mode, other connections to the server are blocked during debugging, and all data modifications are saved after debugging.
Example:
(1) Debugging within redis
# Asynchronous debugging, fallback modified data redis> script debug yes OK # Test script redis> eval "local a = 1; \n local b = 2; \n return 3" 0 * Stopped at 1, stop reason = step over -> 1 local a = 1; # Use n step debugging redis> n * Stopped at 2, stop reason = step over -> 2 local b = 2; redis> n * Stopped at 3, stop reason = step over -> 3 return 3 redis> n (integer) 3 (Lua debugging session ended -- dataset changes rolled back)
(2) In-line debugging
Using the example in the first section (3), add --ldb to the command to enter debug mode: redis-cli --ldb --eval. /member.lua nums, 12
- Once in debug mode, step through n or s until the end
- The default is asynchronous mode, which rolls back modified data after debugging
- Use --ldb-sync-mode to enter synchronization mode, where other links to the server are blocked and the modified data is saved after debugging
Debugging example: [Reference: http://blog.huangz.me/2017/redis-lua-debuger-introduction.html]
$ redis-cli --ldb --eval ./member.lua nums , 1 2 Lua debugging session started, please use: quit -- End the session. restart -- Restart the script in debug mode again. help -- Show Lua script debugging commands. * Stopped at 2, stop reason = step over -> 2 local key = KEYS[1] lua debugger> n * Stopped at 5, stop reason = step over -> 5 local args = ARGV lua debugger> n * Stopped at 8, stop reason = step over -> 8 local result = {} lua debugger> n * Stopped at 11, stop reason = step over -> 11 for m, n in ipairs(args) do lua debugger> n * Stopped at 12, stop reason = step over -> 12 local is_repeat = redis.call("sismember", key, n) lua debugger> n <redis> sismember nums 1 <reply> 1 * Stopped at 13, stop reason = step over -> 13 if (is_repeat) then lua debugger> n * Stopped at 14, stop reason = step over -> 14 table.insert(result, 1, n) lua debugger> n * Stopped at 11, stop reason = step over -> 11 for m, n in ipairs(args) do lua debugger> n * Stopped at 12, stop reason = step over -> 12 local is_repeat = redis.call("sismember", key, n) lua debugger> n <redis> sismember nums 2 <reply> 1 * Stopped at 13, stop reason = step over -> 13 if (is_repeat) then lua debugger> n * Stopped at 14, stop reason = step over -> 14 table.insert(result, 1, n) lua debugger> n * Stopped at 11, stop reason = step over -> 11 for m, n in ipairs(args) do lua debugger> n * Stopped at 17, stop reason = step over -> 17 return result lua debugger> n 1) "2" 2) "1" (Lua debugging session ended -- dataset changes rolled back)