All Products
Search
Document Center

Tair (Redis® OSS-Compatible):Lua scripting in Tair (Redis OSS-compatible)

Last Updated:Mar 01, 2026

This document covers Lua script commands, performance optimization, error handling, and cluster-specific limitations in Tair (Redis OSS-compatible). Lua scripts run atomically on the server, making them well suited for compare-and-swap (CAS) operations and multi-step workflows that would otherwise require multiple round trips.

Lua commands

For full command documentation, see the Redis official website.

CommandSyntaxDescription
EVALEVAL script numkeys [key [key ...]] [arg [arg ...]]Run a Lua script with the specified keys and arguments. Returns the script output.
EVALSHAEVALSHA sha1 numkeys key [key ...] arg [arg ...]Run a cached script by its SHA1 digest. Returns a NOSCRIPT error if the script is not in the cache.
SCRIPT LOADSCRIPT LOAD scriptCache a script and return its SHA1 digest.
SCRIPT EXISTSSCRIPT EXISTS script [script ...]Check whether scripts exist in the cache by SHA1 digest. Returns 1 (exists) or 0 (not found) for each.
SCRIPT KILLSCRIPT KILLTerminate the currently running Lua script.
SCRIPT FLUSHSCRIPT FLUSHRemove all Lua scripts from the script cache.

EVAL parameters

ParameterDescription
scriptThe Lua script to run.
numkeysThe number of keys in the KEYS array. Must be a non-negative integer.
KEYS[]Keys passed to the script. Indexes start from 1.
ARGV[]Additional arguments passed to the script. Indexes start from 1.
EVAL also caches the script, similar to SCRIPT LOAD.
Incorrect usage of KEYS[] and ARGV[], such as passing keys in ARGV[] or hardcoding key names in the script, causes unexpected behavior -- especially in cluster architecture. See Cluster instance constraints.
Always pass values through KEYS[] and ARGV[] instead of encoding them as constants in the script. Hardcoded constants create unique scripts that each consume memory in the Lua interpreter. In a worst-case scenario, this causes an out-of-memory (OOM) error and data loss.

Command examples

Before running these examples, set the test key:

SET foo value_test

EVAL

EVAL "return redis.call('GET', KEYS[1])" 1 foo

Output:

"value_test"

SCRIPT LOAD

SCRIPT LOAD "return redis.call('GET', KEYS[1])"

Output:

"620cd258c2c9c88c9d10db67812ccf663d96bdc6"

EVALSHA

EVALSHA 620cd258c2c9c88c9d10db67812ccf663d96bdc6 1 foo

Output:

"value_test"

SCRIPT EXISTS

SCRIPT EXISTS 620cd258c2c9c88c9d10db67812ccf663d96bdc6 ffffffffffffffffffffffffffffffffffffffff

Output:

1) (integer) 1
2) (integer) 0

SCRIPT FLUSH

Warning

This command deletes all cached Lua scripts from the instance. Back up your Lua scripts before running this command.

SCRIPT FLUSH

Output:

OK

Reduce memory and network overhead

When multiple scripts with duplicate logic are cached separately, they waste memory and can cause an OOM error.

Problem -- hardcoded constants create separate cache entries:

EVAL "return redis.call('set', 'k1', 'v1')" 0
EVAL "return redis.call('set', 'k2', 'v2')" 0

Solution 1 -- use KEYS and ARGV so the same script is cached 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

Solution 2 -- cache the script once with SCRIPT LOAD, then call it with EVALSHA:

SCRIPT LOAD "return redis.call('set', KEYS[1], ARGV[1])"
# Returns: "55b22c0d0cedf3866879ce7c854970626dcef0c3"

EVALSHA 55b22c0d0cedf3866879ce7c854970626dcef0c3 1 k1 v1
EVALSHA 55b22c0d0cedf3866879ce7c854970626dcef0c3 1 k2 v2

Manage the Lua script cache

The Lua script cache consumes instance memory. When used memory approaches or exceeds the limit, the instance returns an OOM error:

-OOM command not allowed when used memory > 'maxmemory'.

To flush the cache, run SCRIPT FLUSH on the client.

BehaviorDetail
SCRIPT FLUSH blockingSynchronous. If many scripts are cached, this command blocks the instance for an extended period. Run it during off-peak hours.
Purge Data in the consoleClears data but does not flush the Lua script cache.
Data evictionDoes not evict the Lua script cache, regardless of the eviction policy. The default eviction mode is volatile-lru.

Avoid writing Lua scripts that consume excessive memory or process large datasets. For details on eviction policies, see data eviction.

Handle NOSCRIPT errors

When EVALSHA targets a script that is not in the cache, the instance returns:

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

Cause: The script was never loaded, or it was cleared during an instance migration, configuration change, version upgrade, or instance switchover. Tair (Redis OSS-compatible) cannot guarantee persistence or replication of cached Lua scripts across these events.

Solution: Run EVAL or SCRIPT LOAD to re-cache the script. Your client should handle the NOSCRIPT error gracefully.

The redis-py library provides a Script class that handles NOSCRIPT errors automatically.

The following Python example shows manual NOSCRIPT handling. The script prepends a string to an existing key value:

import redis
import hashlib

# Returns the SHA1 hex digest of a string.
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.
            return self.evalsha(script_sha1, 1, key, value) # If cached, use EVALSHA.
        else:
            return self.eval(script_content, 1, key, value) # Otherwise, use EVAL (which also caches the script).

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 Lua script timeouts (BUSY error)

Lua scripts run atomically. A slow script blocks the entire instance. After 5 seconds of blocking, the instance returns a BUSY error for all other commands:

BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE.

Solution: Run SCRIPT KILL to terminate the script, or wait for it to finish.

SCRIPT KILL does not take effect during the first 5 seconds of script execution because the instance is blocked.
To prevent extended blocking, estimate execution time before deploying a script, check for infinite loops, and split large scripts if necessary.

Handle UNKILLABLE errors

If a Lua script has already run write commands, SCRIPT KILL fails:

(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 target instance and click restart.

Script persistence and replication

Tair (Redis OSS-compatible) keeps Lua scripts cached as long as the instance is not restarted and SCRIPT FLUSH is not run. However, Tair cannot guarantee script persistence or synchronization to other nodes during:

  • Instance migrations

  • Configuration changes

  • Version upgrades

  • Instance switchovers

Solution: Store all Lua scripts on your on-premises device. Re-cache them with EVAL or SCRIPT LOAD as needed. This prevents NOSCRIPT errors after an instance restart or high availability (HA) switchover.

Cluster instance constraints

General constraints

The following constraints apply to all cluster instances:

ConstraintDetail
Single-shard executionA Lua script runs on a single shard. It cannot be split across shards. The instance routes the script based on the keys specified in the command.
Minimum one keyInclude at least one key in cluster mode.
Same hash slotAll keys in a single Lua script must belong to the same hash slot.
Keyless commandsCommands such as KEYS, SCAN, and FLUSHDB run on a single shard and return only that shard's data.
SCRIPT LOAD scopeRunning SCRIPT LOAD on one node does not guarantee that the script is available on other nodes.

Proxy mode restrictions

The proxy performs its own syntax checks on Lua scripts before forwarding them. These checks are stricter than the Lua interpreter on data nodes.

RestrictionDetail
UNPACKNot supported in proxy mode.
MULTI/EXEC transactionsEVAL, EVALSHA, and SCRIPT commands are not supported inside MULTI/EXEC transactions.

Set the script_check_enable parameter to 0 to disable these additional proxy checks. See modify instance parameters.

If the readonly_lua_route_ronode_enable parameter is set to 1 for a read/write splitting instance, the proxy checks whether Lua scripts contain only read-only commands and determines whether to forward them to read-only nodes. This check imposes additional limits on Lua syntax.

Impact of setting script_check_enable to 0:
Redis 5.0 (minor version earlier than 5.0.8) and 4.0 or earlier: Not recommended. The proxy may return normal results even when a script does not run correctly.
Other versions: The proxy stops checking Lua syntax, but data nodes still perform their own checks.

Cluster architecture errors

Error: -ERR for redis cluster, eval/evalsha number of keys can't be negative or zero\r\n

Cause: Lua scripts in cluster architecture must include at least one key so the proxy can determine which shard to use.

# Valid
EVAL "return redis.call('get', KEYS[1])" 1 fooeval

# Invalid -- no key specified
EVAL "return redis.call('get', 'foo')" 0

Error: -ERR 'xxx' command keys must in same slot

Cause: All keys must hash to the same slot. Use hash tags (for example, {foo}) to guarantee slot alignment.

# Valid -- {foo} hash tag ensures same slot
EVAL "return redis.call('mget', KEYS[1], KEYS[2])" 2 foo {foo}bar

# Invalid -- foo and foobar may hash to different slots
EVAL "return redis.call('mget', KEYS[1], KEYS[2])" 2 foo foobar

Proxy syntax check errors

Disable these checks by setting script_check_enable to 0. See modify instance parameters.

Error: -ERR bad lua script for redis cluster, nested redis.call/redis.pcall

Cause: Nested redis.call/redis.pcall calls are not allowed. Use local variables instead.

# Valid
EVAL "local value = redis.call('GET', KEYS[1]); redis.call('SET', KEYS[2], value)" 2 foo bar

# Invalid -- nested call
EVAL "redis.call('SET', KEYS[1], redis.call('GET', KEYS[2]))" 2 foo bar

Error: -ERR bad lua script for redis cluster, first parameter of redis.call/pcall must be a single literal string

Cause: The command name in redis.call/redis.pcall must be a string literal, not a variable.

# Valid
eval "redis.call('GET', KEYS[1])" 1 foo

# Invalid -- command name stored in a variable
eval "local cmd = 'GET'; redis.call(cmd, KEYS[1])" 1 foo

Version-specific errors

The following errors apply only to:

  • Redis Open-Source Edition 5.0 (minor version earlier than 5.0.8) or 4.0 and earlier

  • Cloud-native edition proxy versions earlier than 7.0.2

  • Classic edition proxy versions earlier than 6.8.12

If you run a version higher than those listed above but still encounter these errors, modify any proxy parameter (such as query_cache_expire) and wait 1 minute before retrying.

Error: -ERR bad lua script for redis cluster, all the keys that the script uses should be passed using the KEYS array\r\n

Cause: All keys in redis.call/redis.pcall must come from the KEYS array. Lua variables, string literals, and ARGV are not allowed as key references.

# Valid
EVAL "return redis.call('mget', KEYS[1], KEYS[2])" 2 foo {foo}bar

# Invalid -- key as string literal
EVAL "return redis.call('mget', KEYS[1], '{foo}bar')" 1 foo

# Invalid -- variable index for KEYS (not allowed in proxy mode; allowed in direct connection mode)
EVAL "local i = 2 return redis.call('mget', KEYS[1], KEYS[i])" 2 foo {foo}bar

# Invalid -- ARGV used as a key
EVAL "return redis.call('mget', KEYS[1], ARGV[1])" 1 foo {foo}bar

The following table lists additional version-specific errors related to specific commands:

ErrorCause
-ERR bad lua script for redis cluster, all the keys that the script uses should be passed using the KEYS array, include destination, and KEYS should not be in expressionThe destination parameter of ZUNIONSTORE and ZINTERSTORE must be specified through the KEYS array.
-ERR bad lua script for redis cluster, ZUNIONSTORE/ZINTERSTORE numkeys parameter should be a single number and not expressionThe numkeys parameter of ZUNIONSTORE/ZINTERSTORE must be a numeric constant, not an expression.
-ERR bad lua script for redis cluster, ZUNIONSTORE/ZINTERSTORE numkeys value is not an integer or out of rangeThe numkeys parameter of ZUNIONSTORE/ZINTERSTORE is not a valid integer.
-ERR bad lua script for redis cluster, ZUNIONSTORE/ZINTERSTORE all the keys that the script uses should be passed using the KEYS arrayAll keys in ZUNIONSTORE/ZINTERSTORE must come from the KEYS array.
-ERR bad lua script for redis cluster, XREAD/XREADGROUP all the keys that the script uses should be passed using the KEYS arrayAll keys in XREAD/XREADGROUP must come from the KEYS array.
-ERR bad lua script for redis cluster, all the keys that the script uses should be passed using the KEYS array, and KEYS should not be in expression, sort command store key does not meet the requirementsAll keys in SORT commands must come from the KEYS array.

Read/write permission errors

ErrorCause
-ERR Write commands are not allowed from read-only scriptsLua scripts sent with EVAL_RO cannot contain write commands.
-ERR bad write command in no write privilegeLua scripts sent from a read-only account cannot contain write commands.

Unsupported command errors

ErrorCause
-ERR script debug not supportSCRIPT DEBUG is not supported in proxy mode.
-ERR bad lua script for redis cluster, redis.call/pcall unkown redis command xxxThe script uses a command not supported in proxy mode. See limits on commands for cluster architecture and read/write splitting instances.

Lua syntax errors

ErrorCause
-ERR bad lua script for redis cluster, redis.call/pcall expect '(' or -ERR bad lua script for redis cluster, redis.call/redis.pcall definition is not complete, expect ')'Missing parentheses in redis.call. Both ( and ) are required.
-ERR bad lua script for redis cluster, at least 1 input key is needed for ZUNIONSTORE/ZINTERSTOREThe numkeys parameter of ZUNIONSTORE/ZINTERSTORE must be greater than 0.
-ERR bad lua script for redis cluster, ZUNIONSTORE/ZINTERSTORE key count < numkeysThe number of keys provided is less than the numkeys value.
-ERR bad lua script for redis cluster, xread/xreadgroup command syntax errorIncorrect XREAD or XREADGROUP syntax. Check the parameter count.
-ERR bad lua script for redis cluster, xread/xreadgroup command syntax error, streams must be specifiedThe STREAMS keyword is required in XREAD and XREADGROUP commands.
-ERR bad lua script for redis cluster, sort command syntax errorIncorrect SORT command syntax.

FAQ

Does Data Management Service (DMS) support Lua scripts?

No. The DMS console does not support Lua script commands. Use a Redis client or redis-cli to connect to your instance and run Lua scripts.