The introduction

Redis is a high-performance key-value database that largely overcomes the limitations of memcached key/value storage and, in some scenarios, complements relational databases. Redis has become the key-value storage system of choice in current architectural designs thanks to its ultra-high performance and rich data structures.

Although there are more than 200 commands available on the Redis website, you can’t help but call Redis multiple times to implement one small step of business logic.

Take the Compare and Set scenario as an example. If you use the Redis native command, you need to obtain the key from Redis, and then extract the value for comparison. If the value is equal, no processing is done. Set the key to the target value if it is not equal or if the key does not exist. A single point of compare and set requires two communications with Redis.

In addition, this decentralized operation cannot take advantage of the atomic nature of Redis and occupy multiple network IO.

Today we’re going to talk about how to handle these situations gracefully.

Redis and Lua

Before introducing Lua, we need to take a look at the language. Lua is a small scripting language that runs on almost every operating system and platform. We don’t typically use Lua for particularly complex transactions, so you just need to understand some of Lua’s basic syntax.

When Redis came out, its developers realized the problems mentioned in the beginning, so Redis started supporting Lua scripting in version 2.6. The new version of Redis also supports Lua Script debugging, which can be found in Documentation and QuickStart.

With Lua scripting, you can achieve significant improvements in the following areas when using the Redis program:

  • Reduce network overhead: the operation of N network requests can be completed with one request. The logic of the original N requests is completed on Redis server, which reduces the network round-trip delay.
  • Atomic operations: Redis executes the entire script as a whole, with no other commands inserted in between. This is an important feature, so be sure to keep a small notebook and memorize it. We’ll talk about why it’s an atomic operation later;
  • Reuse: Scripts sent by clients are permanently stored in Redis. This allows other clients to reuse the script without having to use code to complete the same logic.

So now there is a saying: to learn Redis well, you must know Lua Script.

Compare and set with Lua script

Now let’s implement a simple compare and set, and use this example to feel the new experience of using Lua script for Redis.

Let’s start by looking at how Redis executes Lua scripts.

3.1 Redis的EVAL

Redis 127.0.0.1:6379> EVAL script numkeys key [key...  arg [arg ...]Copy the code
  • Script: The argument is a Lua 5.1 script. A script need not (and should not) be defined as a Lua function.
  • Numkeys: Specifies the number of key name parameters.
  • key [key …] : Indicates the Redis key used in the script, starting with the third argument to EVAL. In Lua, these key-name parameters can be accessed with a base address of 1 through the global KEYS array (KEYS[1], KEYS[2], and so on).
  • arg [arg …] : additional arguments, accessed in Lua through the ARGV array of global variables, in a form similar to KEYS (ARGV[1], ARGV[2], and so on).

Here is an example from the official website.

Figure 1

The above script returns the input parameter directly.

  • Eval is the Redis keyword;
  • The first quote is the Lua script;
  • 2 is the number of parameters;
  • KEYS[1] and KEYS[2] are inputs to KEYS[1] and KEYS[2].
  • First and second are inputs to ARGV[1] and ARGV[2].

KEYS[1],KEYS[2], ARGV[1],ARGV[2] are simply placeholders.

3.2 Executing script files and caching scripts

If you could only write scripts on the command line, wouldn’t it be crazy to run into complicated scripts?

Let’s take a look at how Redis can execute Lua scripts and also verify that Lua scripts can be reused (we no longer need to batch delete certain keys that conform to certain rules periodically).

Redis 127.0.0.1:6379> SCRIPT LOAD SCRIPT Redis 127.0.0.1:6379> EVALSHA sha1 numkeys key [key...  arg [arg ...]Copy the code

Redis provides a SCRIPTLOAD command and the script behind the command is the Lua script. The command adds the script to the script cache, but does not execute it immediately. After the command is executed, Redis returns a SHA1 string, and the second EVALSHA command can be executed.

Note that scripts can remain in the cache for an unlimited amount of time until SCRIPT FLUSH has been executed. So let’s see what happens.

Figure 2

Redis also supports direct execution of Lua script files. Start by writing and storing a Lua script.

Figure 3

Then call the redis-cli-eval command

Figure 4.

The syntax of the redis-cli-eval command is basically the same as that of the original eval command.

3.3 Using the Lua script to compare and set

The compareand set implementation logic looks like this: first get the value of the specified key in Redis, then compare it with the given value: if equal, set the key to the target value and return an identifier; If not, nothing is done and an identifier is returned.

if Redis.call('get', KEYS[1]) == ARGV[1]  then
     Redis.call('set', KEYS[1], ARGV[2]);
     return 1
else
     return 0 end
Copy the code

Let’s test this script.

First write a value value to Redis’ specified key compareAndSet:key

Figure 5

Execute the Lua script in Redis

Figure 6.

The first execution returns 1, indicating that the modification succeeded. If 0 is returned when the original parameter is used, no changes are made. CompareAndSet :key

Figure 7.

You can see compareAndSet:key has been changed to new_value.

conclusion

We implemented a simple compareAndSet operation using a Lua script.

Let’s use this example to verify the feature mentioned above.

  • Reduce network overhead: While implementing a compareAndSet without scripting required at least two interactions with Redis, it now requires only one operation.
  • Atomic operations: Thanks to Redis’s design, the entire script is executed as a whole without being interjected by other commands. Therefore, in the process of writing scripts, there is no need to worry about race conditions, no need to use transactions, interested in Baidu or wait for the subsequent article update;
  • Reuse: You can encapsulate a series of operations into a Lua script, store it in a file or on Redis, and call it the next time you use it.

Hopefully, you’ve got some knowledge of Redis+Lua and can do some simple compositing with scripts. In the future, we will continue to update some distributed data structures based on Lua script + Java program, such as delay queue, reentrant lock, etc., interested partners can continue to pay attention to.

Author: Li Chong

UAVStack intelligent operation and maintenance

Source: Creditease Institute of Technology