Redis Lua Scripting

RedisRedisBeginner
Practice Now

Introduction

In this lab, you will explore Redis Lua Scripting, focusing on executing Lua scripts directly within Redis to perform complex operations efficiently. This lab covers using the EVAL command to execute scripts, passing arguments to scripts, loading scripts with SCRIPT LOAD, and running loaded scripts with EVALSHA.

We'll start by executing a simple Lua script that increments a Redis counter using EVAL, demonstrating how to specify the script, the number of keys it accesses, and the key names. Then, we'll learn how to load scripts into Redis and execute them using their SHA1 hash, improving performance by avoiding repeated script transmission.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL redis(("Redis")) -.-> redis/RedisGroup(["Redis"]) redis/RedisGroup -.-> redis/access_cli("Connect Using CLI") redis/RedisGroup -.-> redis/store_string("Set String Value") redis/RedisGroup -.-> redis/fetch_string("Get String Value") redis/RedisGroup -.-> redis/increment_int("Increase Integer Value") subgraph Lab Skills redis/access_cli -.-> lab-552099{{"Redis Lua Scripting"}} redis/store_string -.-> lab-552099{{"Redis Lua Scripting"}} redis/fetch_string -.-> lab-552099{{"Redis Lua Scripting"}} redis/increment_int -.-> lab-552099{{"Redis Lua Scripting"}} end

Execute Lua Script with EVAL

In this step, we will explore how to execute Lua scripts directly within Redis using the EVAL command. This allows you to perform complex operations on your data in a single request, reducing network latency and improving performance.

Before we begin, let's understand the basics of EVAL. The EVAL command takes two main arguments:

  1. The Lua script itself, as a string.
  2. The number of keys the script will access, followed by the actual key names and any additional arguments you want to pass to the script.

Here's a simple example to get you started. We'll create a Lua script that increments a Redis counter and returns the new value.

  1. Open your terminal and connect to the Redis server using the Redis command-line interface (redis-cli).

    redis-cli
  2. Now, let's execute a Lua script using EVAL. This script will increment a counter named mycounter and return the incremented value.

    EVAL "local current = redis.call('INCR', KEYS[1]); return current" 1 mycounter

    Let's break down this command:

    • EVAL "local current = redis.call('INCR', KEYS[1]); return current": This is the Lua script itself. It uses redis.call('INCR', KEYS[1]) to increment the value of the key specified in KEYS[1]. The KEYS array contains the key names passed to the script. Finally, it returns the incremented value.
    • 1: This indicates that the script will access one key.
    • mycounter: This is the name of the key that the script will access.

    You should see output similar to this:

    (integer) 1

    If you run the same command again, you'll see the counter increment:

    EVAL "local current = redis.call('INCR', KEYS[1]); return current" 1 mycounter

    Output:

    (integer) 2
  3. Let's verify the value of the mycounter key using the GET command:

    GET mycounter

    Output:

    "2"

    As you can see, the Lua script successfully incremented the counter.

  4. Now, let's try another example. This time, we'll create a script that sets a key to a specific value if it doesn't already exist.

    EVAL "if redis.call('EXISTS', KEYS[1]) == 0 then redis.call('SET', KEYS[1], ARGV[1]); return 1 else return 0 end" 1 mykey myvalue

    In this command:

    • EVAL "if redis.call('EXISTS', KEYS[1]) == 0 then redis.call('SET', KEYS[1], ARGV[1]); return 1 else return 0 end": This is the Lua script. It checks if the key KEYS[1] exists. If it doesn't, it sets the key to the value ARGV[1] and returns 1. Otherwise, it returns 0.
    • 1: This indicates that the script will access one key.
    • mykey: This is the name of the key.
    • myvalue: This is the value to set if the key doesn't exist.

    You should see output similar to this:

    (integer) 1

    This indicates that the key mykey was set to myvalue.

  5. Let's check the value of mykey:

    GET mykey

    Output:

    "myvalue"

    If you run the EVAL command again:

    EVAL "if redis.call('EXISTS', KEYS[1]) == 0 then redis.call('SET', KEYS[1], ARGV[1]); return 1 else return 0 end" 1 mykey myvalue

    Output:

    (integer) 0

    This time, the script returned 0 because the key already existed.

  6. Exit the redis-cli. This is important for the changes to be logged.

    exit

Pass Arguments to Script with EVAL

In the previous step, we learned how to execute Lua scripts with EVAL and access keys. Now, let's explore how to pass arguments to these scripts. This allows for more dynamic and reusable scripts.

Recall that the EVAL command takes the following arguments:

  1. The Lua script itself, as a string.
  2. The number of keys the script will access.
  3. The key names.
  4. Any additional arguments you want to pass to the script. These arguments are accessed within the Lua script using the ARGV array.

Let's start with an example. We'll create a Lua script that adds a value to a Redis counter. The counter's key and the value to add will be passed as arguments.

  1. Connect to the Redis server using the Redis command-line interface (redis-cli).

    redis-cli
  2. Now, let's execute a Lua script using EVAL that increments a counter by a specified amount.

    EVAL "local current = redis.call('INCRBY', KEYS[1], ARGV[1]); return current" 1 mycounter 5

    Let's break down this command:

    • EVAL "local current = redis.call('INCRBY', KEYS[1], ARGV[1]); return current": This is the Lua script. It uses redis.call('INCRBY', KEYS[1], ARGV[1]) to increment the value of the key specified in KEYS[1] by the amount specified in ARGV[1].
    • 1: This indicates that the script will access one key.
    • mycounter: This is the name of the key that the script will access.
    • 5: This is the argument that will be passed to the script and accessed as ARGV[1].

    You should see output similar to this (assuming mycounter was at 2 from the previous step):

    (integer) 7

    The script incremented the mycounter key by 5.

  3. Let's verify the value of the mycounter key using the GET command:

    GET mycounter

    Output:

    "7"
  4. Now, let's try another example with string arguments. We'll create a script that sets a key to a value, where both the key and the value are passed as arguments.

    EVAL "redis.call('SET', KEYS[1], ARGV[1]); return ARGV[1]" 1 mynewkey mynewvalue

    In this command:

    • EVAL "redis.call('SET', KEYS[1], ARGV[1]); return ARGV[1]": This is the Lua script. It sets the key KEYS[1] to the value ARGV[1] and returns the value.
    • 1: This indicates that the script will access one key.
    • mynewkey: This is the name of the key.
    • mynewvalue: This is the value to set.

    You should see output similar to this:

    "mynewvalue"
  5. Let's check the value of mynewkey:

    GET mynewkey

    Output:

    "mynewvalue"

    The script successfully set the key mynewkey to the value mynewvalue.

  6. You can pass multiple arguments to the script. For example, let's create a script that concatenates two strings passed as arguments and sets the result to a key.

    EVAL "local result = ARGV[1] .. ARGV[2]; redis.call('SET', KEYS[1], result); return result" 1 combinedkey hello world

    In this command:

    • EVAL "local result = ARGV[1] .. ARGV[2]; redis.call('SET', KEYS[1], result); return result": This is the Lua script. It concatenates ARGV[1] and ARGV[2] using the .. operator, sets the key KEYS[1] to the result, and returns the result.
    • 1: This indicates that the script will access one key.
    • combinedkey: This is the name of the key.
    • hello: This is the first string argument.
    • world: This is the second string argument.

    You should see output similar to this:

    "helloworld"
  7. Let's check the value of combinedkey:

    GET combinedkey

    Output:

    "helloworld"
  8. Exit the redis-cli. This is important for the changes to be logged.

    exit

Load Script with SCRIPT LOAD

In the previous steps, we executed Lua scripts directly using the EVAL command. While this is useful for simple scripts, it can become cumbersome for larger, more complex scripts. Redis provides the SCRIPT LOAD command to load scripts into the Redis server's script cache. This allows you to execute the script multiple times without having to send the entire script each time, improving performance.

The SCRIPT LOAD command takes a single argument: the Lua script itself. It returns the SHA1 hash of the script, which you can then use to execute the script using the EVALSHA command (which we'll cover in the next step).

Let's see how it works.

  1. Connect to the Redis server using the Redis command-line interface (redis-cli).

    redis-cli
  2. Now, let's load a Lua script using SCRIPT LOAD. We'll use the same script from the previous step that increments a counter by a specified amount.

    SCRIPT LOAD "local current = redis.call('INCRBY', KEYS[1], ARGV[1]); return current"

    This command will load the script into the Redis server's script cache and return the SHA1 hash of the script. You should see output similar to this:

    "6b1e8dd2999cb08546e74339c0c9489f9f89a84b"

    This is the SHA1 hash of the script. Make a note of this hash, as you'll need it in the next step. The exact hash value may differ.

  3. Now, let's load the script that sets a key to a value, where both the key and the value are passed as arguments.

    SCRIPT LOAD "redis.call('SET', KEYS[1], ARGV[1]); return ARGV[1]"

    You should see output similar to this:

    "a8b2b3648969459a8198262a9166e945e890987c"

    Again, make a note of this hash.

  4. Let's load the script that concatenates two strings passed as arguments and sets the result to a key.

    SCRIPT LOAD "local result = ARGV[1] .. ARGV[2]; redis.call('SET', KEYS[1], result); return result"

    You should see output similar to this:

    "d2a800a974ca96849295220424f9a0664a495345"

    Make a note of this hash as well.

  5. You can verify that the scripts are loaded using the SCRIPT EXISTS command. This command takes one or more SHA1 hashes as arguments and returns an array of 0s and 1s, where 1 indicates that the script with the corresponding hash is loaded and 0 indicates that it is not.

    For example, to check if the first script we loaded is still loaded, use the following command, replacing the hash with the one you obtained in step 2:

    SCRIPT EXISTS 6b1e8dd2999cb08546e74339c0c9489f9f89a84b

    Output:

    1) (integer) 1

    This indicates that the script is loaded.

    If you try to check for a script that is not loaded:

    SCRIPT EXISTS 0000000000000000000000000000000000000000

    Output:

    1) (integer) 0

    This indicates that the script is not loaded.

  6. Exit the redis-cli. This is important for the changes to be logged.

    exit

Run Loaded Script with EVALSHA

In the previous step, we learned how to load Lua scripts into the Redis server's script cache using the SCRIPT LOAD command. Now, we'll learn how to execute those loaded scripts using the EVALSHA command.

The EVALSHA command takes the following arguments:

  1. The SHA1 hash of the loaded script.
  2. The number of keys the script will access.
  3. The key names.
  4. Any additional arguments you want to pass to the script.

Using EVALSHA is more efficient than EVAL when you need to execute the same script multiple times, as it avoids sending the entire script to the server each time.

Let's see how it works.

  1. Connect to the Redis server using the Redis command-line interface (redis-cli).

    redis-cli
  2. Now, let's execute the Lua script that increments a counter by a specified amount using EVALSHA. Remember the SHA1 hash you obtained in the previous step for the script local current = redis.call('INCRBY', KEYS[1], ARGV[1]); return current? If not, you'll need to load the script again using SCRIPT LOAD. For this example, we'll assume the hash is 6b1e8dd2999cb08546e74339c0c9489f9f89a84b.

    EVALSHA 6b1e8dd2999cb08546e74339c0c9489f9f89a84b 1 mycounter 5

    Let's break down this command:

    • EVALSHA 6b1e8dd2999cb08546e74339c0c9489f9f89a84b: This specifies that we want to execute the script with the SHA1 hash 6b1e8dd2999cb08546e74339c0c9489f9f89a84b.
    • 1: This indicates that the script will access one key.
    • mycounter: This is the name of the key that the script will access.
    • 5: This is the argument that will be passed to the script and accessed as ARGV[1].

    You should see output similar to this (assuming mycounter was at 7 from the previous step):

    (integer) 12

    The script incremented the mycounter key by 5.

  3. Let's verify the value of the mycounter key using the GET command:

    GET mycounter

    Output:

    "12"
  4. Now, let's execute the script that sets a key to a value using EVALSHA. Remember the SHA1 hash you obtained in the previous step for the script redis.call('SET', KEYS[1], ARGV[1]); return ARGV[1]? If not, you'll need to load the script again using SCRIPT LOAD. For this example, we'll assume the hash is a8b2b3648969459a8198262a9166e945e890987c.

    EVALSHA a8b2b3648969459a8198262a9166e945e890987c 1 anotherkey anothervalue

    You should see output similar to this:

    "anothervalue"
  5. Let's check the value of anotherkey:

    GET anotherkey

    Output:

    "anothervalue"
  6. If you try to execute a script with an invalid SHA1 hash, you'll get an error:

    EVALSHA 0000000000000000000000000000000000000000 1 mykey myvalue

    Output:

    (error) NOSCRIPT No matching script. Please use EVAL.

    This indicates that the script with the specified SHA1 hash is not loaded.

  7. Exit the redis-cli. This is important for the changes to be logged.

    exit

Summary

In this lab, you have explored Redis Lua scripting, focusing on executing scripts directly using the EVAL command. We learned that EVAL takes the Lua script as a string, the number of keys accessed, and the key names themselves as arguments. We practiced incrementing a Redis counter using a Lua script within EVAL, observing how the script accessed and modified the specified key.

Furthermore, we learned how to pass arguments to Lua scripts, load scripts into Redis using SCRIPT LOAD, and execute loaded scripts using EVALSHA. Remember to exit the redis-cli after each step to ensure your commands are logged correctly. This allows for more efficient execution of complex scripts.