Show respect for other people’s opinions. Don’t say: “You’re wrong.” – Carnegie

Lua is a lightweight scripting language written in standard C and open as source code. It is designed to be embedded in applications to provide flexible extension and customization capabilities. Due to the atomicity of Lua, it is not interrupted by other programs during execution, which is helpful for data consistency under concurrent conditions.

About the author: May Jun, Nodejs Developer, moOCnet certified author, love technology, love to share the post-90s youth, welcome to pay attention to Nodejs technology stack and Github open source project www.nodejs.red

Two Lua scripts for Redis

Redis supports two ways to run Lua scripts. One is to directly enter Lua code in Redis, which is suitable for some simple scripts. Another way is to write Lua Script files, suitable for logical operations, Redis uses SHA1 algorithm to support Script signature and Script Load pre-cache, need to run through the identifier returned by the signature.

The following sections describe how to apply the EVAL and EVALSHA commands provided by Redis to Lua scripts, and how to apply the Lua scripts in Node.js.

EVAL

Starting with Redis 2.6.0, Lua scripts can be evaluated using the EVAL command through the built-in Lua interpreter

  • Script: execute the script
  • Numkeys: specifies the number of key name parameters
  • Key: specifies the name of a key, which can be multiple (key1 or key2). The key can be accessed by KEYS[1] KEYS[2]
  • Atg: key value, can be multiple (val1, val2), through ARGS[1] ARGS[2]
EVAL script numkeys key [key ...] arg [arg. ]Copy the code

EVAL Redis console practice

Write an example of the command as follows: access ARGV[] as an array of KEYS[], starting with 1, KEYS[1] with name1, and ARGV[2] with val2

127.0. 01.:6379> EVAL "return redis.call('SET', KEYS[1], ARGV[2])" 2 name1 name2 val1 val2
OK
Copy the code

Run the preceding command to check that the value of name1 is val2 through get

127.0.0.1:6379 > get name1"val2"
Copy the code

Note: The above command will return (nil) without return.

127.0. 01.:6379> EVAL "redis.call('SET', KEYS[1], ARGV[2])" 2 name1 name2 val1 val2
(nil)
Copy the code

redis.call VS redis.pcall

Redis. Call and redis. Pcall are two different Lua functions that call the redis command. Redis. pcall catches and returns errors as Lua.

Use redis. Call

There are two Redis commands executed, the first one intentionally put SET_ which is an error command, and you can see that error message gets thrown to the caller, and you do get name2 and you get nil, and the second one doesn’t get executed

127.0.0.1:6379 > EVAL"redis.call('SET_', KEYS[1], ARGV[2]); redis.call('SET', KEYS[2], ARGV[3])" 2 name1 name2 val1 val2 val3
(error) ERR Error running script (call to f_bf814e38e3d98242ae0c62791fa299f04e757a7d): @user_script:1: @user_script: 1: Unknown Redis command called from Lua script 
Copy the code

Use redis. Pcall

Same operation as above, using redis.pcall we can see that the output is nil and its error is caught by Lua, so when we do get name2 we get a set result val3, where the second command is executed.

EVAL "redis.pcall('SET_', KEYS[1], ARGV[2]); redis.pcall('SET', KEYS[2], ARGV[3])" 2 name1 name2 val1 val2 val3
(nil)
Copy the code

EVAL is implemented in Node.js

Ioredis supports all scripting commands, such as EVAL, EVALSHA, and SCRIPT. However, using it in a real-world scenario can be tedious, as developers must pay attention to script caching and detect when EVAL is used and when EVALSHA is used. Ioredis exposes a defineCommand method to make scripts easier to use.

const Redis = require("ioredis");
const redis = new Redis(6379."127.0.0.1");

const evalScript = `return redis.call('SET', KEYS[1], ARGV[2])`;

redis.defineCommand("evalTest", {
    numberOfKeys: 2.lua: evalScript,
})

async function eval() {
    await redis.evalTest('name1'.'name2'.'val1'.'val2');
    const result = await redis.get('name1');
    console.log(result); // val2
}

eval(a);Copy the code

EVALSHA

The EVAL command requires that you send the script body each time you execute the script. Redis has an internal caching mechanism, so instead of recompiling scripts every time, it uses EVALSHA to evaluate scripts cached in the server for a given SHA1 check code. How do YOU make SHA1? You can manipulate the script cache by using the script command

  • SCRIPT FLUSH: Clears all SCRIPT caches
  • SCRIPT EXISTS: Checks whether the specified SCRIPT EXISTS in the SCRIPT cache
  • SCRIPT LOAD: Loads a SCRIPT into the SCRIPT cache, but does not run it immediately
  • SCRIPT KILL: Kills the currently running SCRIPT

Format of the EVALSHA command

Instead of EVAL, the EVAL script is EVALSHA sha1

EVALSHA sha1 numkeys key [key ...] arg [arg. ]Copy the code

EVALSHA Redis console practice

Loading script cache

127.0. 01.:6379> SCRIPT LOAD "redis.pcall('SET', KEYS[1], ARGV[2]);"
"2a3b189808b36be907e26dab7ddcd8428dcd1bc8"
Copy the code

The above script returns a signed sha-1 identifier string that is used to execute the signed script with the following command

127.0. 01.:6379> EVALSHA 2a3b189808b36be907e26dab7ddcd8428dcd1bc8 2 name1 name2 val1 val2
Copy the code

Get reads name1 only for val2

127.0. 01.:6379> get name1
"val2"
Copy the code

EVALSHA is implemented in Node.js

There are three steps: cache the script, execute the script, and get the data

const Redis = require("ioredis");
const redis = new Redis(6379."127.0.0.1");

const evalScript = `return redis.call('SET', KEYS[1], ARGV[2])`;

async function evalSHA() {
    // 1. Cache script to obtain sha1 value
    const sha1 = await redis.script("load", evalScript);
    console.log(sha1); // 6bce4ade07396ba3eb2d98e461167563a868c661

    // 2. Run the script using evalsha
    await redis.evalsha(sha1, 2.'name1'.'name2'.'val1'.'val2');

    // 3. Obtain data
    const result = await redis.get("name1");
    console.log(result); // "val2"
}

evalSHA();
Copy the code

Lua script file

Logical operation scripts, you can write Lua script files, write some simple scripts are not difficult, you can refer to this tutorial www.runoob.com/lua/lua-tut…

Lua files

The following is a test code, by reading two values to compare the return of different values, through the Lua script can be multiple Redis command atomicity.

-- test.lua

- the first SET
redis.call("SET", KEYS[1], ARGV[1])
redis.call("SET", KEYS[2], ARGV[2])

- the GET accessor
local key1 = tonumber(redis.call("GET", KEYS[1]))
local key2 = tonumber(redis.call("GET", KEYS[2]))

Returns 0 if key1 is less than key2
-- nil equals false
if (key1 == nil or key2 == nil or key1 < key2) 
then 
    return 0
else 
    return 1
end
Copy the code

Load Lua script files in Node.js

The fs module is used to read the Lua script file and execute it using eval or evalsha.

const Redis = require("ioredis");
const redis = new Redis(6379."127.0.0.1");
const fs = require('fs');

async function test() {
    const redisLuaScript = fs.readFileSync('./test.lua');
    const result1 = await redis.eval(redisLuaScript, 2.'name1'.'name2'.20.10);
    const result2 = await redis.eval(redisLuaScript, 2.'name1'.'name2'.10.20);
    console.log(result1, result2); / / 1 0
}

test();
Copy the code