Tair (Redis OSS-compatible) instances support Lua commands. Lua scripts can be used to efficiently process compare-and-set (CAS) commands. This improves the performance of Tair (Redis OSS-compatible) and simplifies the implementation of features. This topic describes the syntax and usage of Lua scripts.
Precautions
Commands related to Lua scripts cannot be used in the Data Management (DMS) console. For more information about DMS, see Overview. You can use a client or redis-cli to connect to instances and use Lua scripts.
Syntax
Command | Syntax | Description |
EVAL |
| Executes a specific script that includes parameters and returns the output. Parameters:
Note
|
EVALSHA |
| Evaluates a cached script by its SHA1 digest and runs the script. If the script is not cached in Tair (Redis OSS-compatible) when you use the EVALSHA command, Tair (Redis OSS-compatible) returns the NOSCRIPT error. Cache the script in Tair (Redis OSS-compatible) by using the EVAL or SCRIPT LOAD command and try again. For more information, see Handle the NOSCRIPT error. |
SCRIPT LOAD |
| Caches a specified script and returns the SHA1 digest of the script. |
SCRIPT EXISTS |
| Returns information about the existence of one or more scripts in the script cache by using their corresponding SHA1 digests. If a specific script exists, a value of 1 is returned. Otherwise, a value of 0 is returned. |
SCRIPT KILL |
| Terminates a running Lua script. |
SCRIPT FLUSH |
| Removes all Lua scripts from the script cache in the current instance. |
For more information about the commands, visit the Redis official website.
The following sample code provides examples of specific Redis commands. Before the following commands are run, the SET foo value_test
command is run.
Sample EVAL command:
EVAL "return redis.call('GET', KEYS[1])" 1 foo
Sample output:
"value_test"
Sample SCRIPT LOAD command:
SCRIPT LOAD "return redis.call('GET', KEYS[1])"
Sample output:
"620cd258c2c9c88c9d10db67812ccf663d96bdc6"
Sample EVALSHA command:
EVALSHA 620cd258c2c9c88c9d10db67812ccf663d96bdc6 1 foo
Sample output:
"value_test"
Sample SCRIPT EXISTS command:
SCRIPT EXISTS 620cd258c2c9c88c9d10db67812ccf663d96bdc6 ffffffffffffffffffffffffffffffffffffffff
Sample output:
1) (integer) 1 2) (integer) 0
Sample SCRIPT FLUSH command:
WarningThis command deletes all cached Lua scripts from the instance. Make sure that you back up the Lua scripts before you run this command.
SCRIPT FLUSH
Sample output:
OK
Reduce memory and network overheads
Problem description:
A large number of scripts that serve the same purposes are cached in the instance. These scripts take up large amounts of memory and may cause an OOM error. Example of invalid usage:
EVAL "return redis.call('set', 'k1', 'v1')" 0
EVAL "return redis.call('set', 'k2', 'v2')" 0
Solution:
Do not pass parameters to Lua scripts as constants to reduce memory usage.
# The following commands serve the same purposes as the preceding sample commands but cache scripts only once. EVAL "return redis.call('set', KEYS[1], ARGV[1])" 1 k1 v1 EVAL "return redis.call('set', KEYS[1], ARGV[1])" 1 k2 v2
Use the following command syntax to reduce memory and network overheads:
SCRIPT LOAD "return redis.call('set', KEYS[1], ARGV[1])" # After this command is run, the following output is returned: "55b22c0d0cedf3866879ce7c854970626dcef0c3". EVALSHA 55b22c0d0cedf3866879ce7c854970626dcef0c3 1 k1 v1 EVALSHA 55b22c0d0cedf3866879ce7c854970626dcef0c3 1 k2 v2
Flush the Lua script cache
Problem description:
Used memory of the instance may be higher than expected because the Lua script cache takes up memory of the instance. When the used memory of the instance approaches or exceeds the upper limit, an OOM error is returned. Error example:
-OOM command not allowed when used memory > 'maxmemory'.
Solution:
Flush the Lua script cache by running the SCRIPT FLUSH command on the client. Different from the FLUSHALL command, the SCRIPT FLUSH command is synchronous. If the instance caches a large number of Lua scripts, the SCRIPT FLUSH command can block the instance for an extended period of time and the involved instance may become unavailable. Proceed with caution. We recommend that you perform this operation during off-peak hours.
If you click Clear Data in the console, data can be cleared but the Lua script cache cannot be flushed.
Do not write Lua scripts that may take up excessive amounts of memory. Moreover, do not write Lua scripts that involve large amounts of data. Otherwise, memory usage significantly increases and an OOM error may occur. To reduce memory usage, we recommend that you enable data eviction by using the volatile-lru policy. By default, data eviction is enabled for the instance. However, the instance does not evict the Lua script cache regardless of whether data eviction is enabled.
Handle the NOSCRIPT error
Problem description:
If the script is not cached in the instance when you use the EVALSHA command, the instance returns the NOSCRIPT error. Error example:
(error) NOSCRIPT No matching script. Please use EVAL.
Solution:
Run the EVAL or SCRIPT LOAD command to cache the script in the instance and try again. In specific scenarios, such as instance migrations and configuration changes, the instance flushes the Lua script cache because the instance cannot ensure the persistence and replicability of Lua scripts. In this case, your client must be able to handle the error. For more information, see Caching, persistence, and replication of scripts.
The following sample Python code shows a method for handling the NOSCRIPT error. The sample code prepends strings by using Lua scripts.
You can also use redis-py to handle the error. redis-py provides the Script class that encapsulates the judgment logic for Lua scripts, such as a catch statement for the NOSCRIPT error.
import redis
import hashlib
# strin specifies a string in Lua scripts. The following function returns the SHA1 value of strin in the string format.
def calcSha1(strin):
sha1_obj = hashlib.sha1()
sha1_obj.update(strin.encode('utf-8'))
sha1_val = sha1_obj.hexdigest()
return sha1_val
class MyRedis(redis.Redis):
def __init__(self, host="localhost", port=6379, password=None, decode_responses=False):
redis.Redis.__init__(self, host=host, port=port, password=password, decode_responses=decode_responses)
def prepend_inLua(self, key, value):
script_content = """\
local suffix = redis.call("get", KEYS[1])
local prefix = ARGV[1]
local new_value = prefix..suffix
return redis.call("set", KEYS[1], new_value)
"""
script_sha1 = calcSha1(script_content)
if self.script_exists(script_sha1)[0] == True: # Check whether the script is already cached in Tair (Redis OSS-compatible).
return self.evalsha(script_sha1, 1, key, value) # If the script is already cached, the EVALSHA command is used to run the script.
else:
return self.eval(script_content, 1, key, value) # Otherwise, use the EVAL command to run the script. Note that the EVAL command can cache scripts. Another method is to use the SCRIPT LOAD and EVALSHA commands.
r = MyRedis(host="r-******.redis.rds.aliyuncs.com", password="***:***", port=6379, decode_responses=True)
print(r.prepend_inLua("k", "v"))
print(r.get("k"))
Handle timeouts of Lua scripts
Problem description:
Slow Lua requests may block the instance because a Lua script is atomically executed in the instance. One Lua script can block the instance for up to 5 seconds. After 5 seconds, the instance returns a BUSY error for other commands until the script execution is complete.
BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE.
Solution:
Run the SCRIPT KILL command to terminate the Lua script or wait until the Lua script finishes running.
NoteDuring the first 5 seconds when a slow Lua script is being executed, the SCRIPT KILL command does not take effect because the instance is being blocked.
To prevent the instance from being blocked for an extended period of time, we recommend that you estimate the amount of time required to execute a Lua script when you write the Lua script, check for infinite loop, and split the Lua script if necessary.
Problem description:
If a Lua script has already run write commands against the dataset, the SCRIPT KILL command does not take effect. Error example:
(error) UNKILLABLE Sorry the script already executed write commands against the dataset. You can either wait the script termination or kill the server in a hard way using the SHUTDOWN NOSAVE command.
Solution:
On the Instances page of the console, find the instance that you want to manage and click restart in the Actions column.
Caching, persistence, and replication of scripts
Problem description:
Tair (Redis OSS-compatible) keeps caching the Lua scripts that have been executed in an instance if the instance is not restarted or the SCRIPT FLUSH command is not run for the instance. However, Tair (Redis OSS-compatible) cannot ensure the persistence of Lua scripts or the synchronization of Lua scripts from the current node to other nodes in scenarios such as instance migrations, configuration changes, version upgrades, and instance switchovers.
Solution:
Store all Lua scripts in your on-premise device. Recache the Lua scripts in Tair (Redis OSS-compatible) by using the EVAL or SCRIPT LOAD command if necessary. This prevents a NOSCRIPT error from occurring when Lua scripts are cleared during an instance restart or a high availability (HA) switchover.
Limits on Lua scripts in cluster instances
To ensure the execution atomicity, a Lua script cannot be split and can be executed only on one shard in a cluster instance. In most cases, a key is used to determine which shard Lua scripts are routed to. Therefore, you must specify at least one key when you run a Lua script in a cluster instance. If you want to read or write multiple keys, the keys in one Lua script must belong to the same slot. Otherwise, an abnormal execution result is returned. Lua scripts that do not have keys (such as KEYS, SCAN, and FLUSHDB) can be executed normally. However, only the data of a single shard is returned. This limit is caused by the architecture of cluster instances.
A Lua script may not be stored in other nodes when you run the SCRIPT LOAD command on one node.
Error codes and causes for custom Lua script errors in proxy mode
Proxy nodes perform syntax checks to identify the keys that belong to multiple slots and throw exceptions in advance to assist troubleshooting. Proxy nodes use a different approach to check for errors than Lua virtual machines. This places additional limits on the execution of Lua scripts in proxy mode. For example, the UNPACK command is not supported, and the EVAL, EVALSHA, and SCRIPT commands are not supported in MULTI and EXEC transactions. You can also set the script_check_enable parameter to disable additional checks on Lua syntax in proxy mode.
How does setting the script_check_enable parameter to 0 affect the instance?
If the instance is compatible with Redis 5.0 (with minor version earlier than 5.0.8) and 4.0 or earlier, we recommend that you do not disable the checks. Otherwise, normal results may be returned when the script is not executed normally.
If you disable the checks on other versions, proxy nodes no longer check Lua syntax, but data nodes still check Lua syntax.
If the readonly_lua_route_ronode_enable parameter is set to 1 for a read/write splitting instance, proxy nodes check whether Lua scripts contain only read-only commands and determine whether to forward them to read-only nodes. This check logic imposes limits on Lua syntax.
The following table describes the error codes and causes.
Category | Error code | Description |
Cluster architecture |
| You must include a key when you execute a Lua script. Proxy nodes use the key to determine the shard to which the Lua script is forwarded.
|
| Multiple keys in a Lua script must belong to the same slot.
| |
Lua syntax check in proxy mode (You can set the script_check_enable parameter to 0 to disable this check.) |
| Nested calls are not supported. You can call the Lua script by using local variables.
|
| When you use Lua scripts with the redis.call or redis.pcall function, the first parameter you provide must be a string literal representing the command that you want to run.
| |
| Lua scripts use the redis.call or redis.pcall function to run commands. All keys must be specified by using the KEYS array, which cannot be replaced by Lua variables. Note This limit applies only to Redis Open-Source Edition instances that run Redis 5.0 (with minor version earlier than 5.0.8) or Redis 4.0 or earlier, cloud-native instances whose proxy version is earlier than 7.0.2, or classic instances whose proxy version is earlier than 6.8.12. If the instance version and proxy version meet the requirements but the limit still exists, modify any parameter such as query_cache_expire. Wait for 1 minute and try again.
| |
| The destination parameter of the ZUNIONSTORE and ZINTERSTORE commands must be specified by using the KEYS array. Note This limit applies only to Redis Open-Source Edition instances that run Redis 5.0 (with minor version earlier than 5.0.8) or Redis 4.0 or earlier, cloud-native instances whose proxy version is earlier than 7.0.2, or classic instances whose proxy version is earlier than 6.8.12. If the instance version and proxy version meet the requirements but the limit still exists, modify any parameter such as query_cache_expire. Wait for 1 minute and try again. | |
| The numkeys parameter of the ZUNIONSTORE and ZINTERSTORE commands is not a constant. Note This limit applies only to Redis Open-Source Edition instances that run Redis 5.0 (with minor version earlier than 5.0.8) or Redis 4.0 or earlier, cloud-native instances whose proxy version is earlier than 7.0.2, or classic instances whose proxy version is earlier than 6.8.12. If the instance version and proxy version meet the requirements but the limit still exists, modify any parameter such as query_cache_expire. Wait for 1 minute and try again. | |
| The numkeys parameter of the ZUNIONSTORE and ZINTERSTORE commands is not a number. Note This limit applies only to Redis Open-Source Edition instances that run Redis 5.0 (with minor version earlier than 5.0.8) or Redis 4.0 or earlier, cloud-native instances whose proxy version is earlier than 7.0.2, or classic instances whose proxy version is earlier than 6.8.12. If the instance version and proxy version meet the requirements but the limit still exists, modify any parameter such as query_cache_expire. Wait for 1 minute and try again. | |
| All keys of the ZUNIONSTORE and ZINTERSTORE commands must be specified by using the KEYS array. Note This limit applies only to Redis Open-Source Edition instances that run Redis 5.0 (with minor version earlier than 5.0.8) or Redis 4.0 or earlier, cloud-native instances whose proxy version is earlier than 7.0.2, or classic instances whose proxy version is earlier than 6.8.12. If the instance version and proxy version meet the requirements but the limit still exists, modify any parameter such as query_cache_expire. Wait for 1 minute and try again. | |
-ERR bad lua script for redis cluster, XREAD/XREADGROUP all the keys that the script uses should be passed using the KEYS array | All keys of the XREAD and XREADGROUP commands must be specified by using the KEYS array. Note This limit applies only to Redis Open-Source Edition instances that run Redis 5.0 (with minor version earlier than 5.0.8) or Redis 4.0 or earlier, cloud-native instances whose proxy version is earlier than 7.0.2, or classic instances whose proxy version is earlier than 6.8.12. If the instance version and proxy version meet the requirements but the limit still exists, modify any parameter such as query_cache_expire. Wait for 1 minute and try again. | |
| The keys of the SORT commands must be specified by using the KEYS array. Note This limit applies only to Redis Open-Source Edition instances that run Redis 5.0 (with minor version earlier than 5.0.8) or Redis 4.0 or earlier, cloud-native instances whose proxy version is earlier than 7.0.2, or classic instances whose proxy version is earlier than 6.8.12. If the instance version and proxy version meet the requirements but the limit still exists, modify any parameter such as query_cache_expire. Wait for 1 minute and try again. | |
Read/write permissions |
| Lua scripts sent by using the EVAL_RO command cannot contain write commands. |
| Lua scripts sent by a read-only account cannot contain write commands. | |
Unsupported commands |
| The SCRIPT DEBUG command is not supported in proxy mode. |
| Lua scripts contain commands that are not supported in proxy mode. For more information, see Limits on commands supported by cluster instances and read/write splitting instances. | |
Lua syntax |
| Lua syntax error: The |
| The numkeys parameter of the ZUNIONSTORE and ZINTERSTORE commands must be greater than 0. | |
| The number of keys in the ZUNIONSTORE and ZINTERSTORE commands is less than the numkeys value. | |
| The XREAD or XREADGROUP command uses incorrect syntax. Check the number of parameters. | |
| The streams parameter is required in the XREAD and XREADGROUP commands. | |
| The SORT command uses incorrect syntax. |