Redis Learning Notes - Lua Scripts - Using Lua Scripts

Posted by harnacks on Mon, 02 Sep 2019 04:10:05 +0200

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

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)

Topics: Redis SHA1 Session network