All Products
Search
Document Center

PolarDB:Lua script usage guide

Last Updated:Mar 28, 2026

Orca, the Redis-compatible protocol in PolarDB for MySQL, supports Lua scripting for atomic execution of multiple commands in a single database interaction. Use Lua scripts to implement distributed locks, rate limiters, and conditional updates—without the latency and race condition risks of multiple round trips.

Prerequisites

Before you begin, make sure you have:

  • A PolarDB cluster running MySQL 8.0.2 with minor engine version 8.0.2.2.33 or later

  • An ORCA Endpoint configured with Read/Write set to Read/Write (Automatic Read/Write Splitting)

Key concepts

Atomicity — All commands in a Lua script execute as a single, indivisible unit. No other command or script can interrupt execution, which prevents partial writes and intermediate state.

Script caching — Load a script once with SCRIPT LOAD to get its SHA1 checksum, then invoke it repeatedly with EVALSHA using only the short SHA1 value. This reduces network traffic compared to sending the full script on every call.

Script cache volatility — The script cache is not persisted. It is cleared after a cluster restart, a high-availability (HA) switchover, or an explicit SCRIPT FLUSH call. Design your application to catch NOSCRIPT errors and reload scripts automatically.

Commands

All Lua scripting commands follow the same parameter convention: pass key names through KEYS[] and other arguments through ARGV[]. Array indices start at 1.

For the full Redis specification, see Scripting with Lua and EVAL command reference on redis.io.

CommandSyntaxDescription
EVALEVAL script numkeys [key [key ...]] [arg [arg ...]]Execute a Lua script directly. numkeys is a non-negative integer specifying how many KEYS[] arguments follow.
EVAL_ROEVAL_RO script numkeys [key [key ...]] [arg [arg ...]]Execute a Lua script in read-only mode. Returns an error if the script contains write commands. Use this for read/write splitting scenarios.
EVALSHAEVALSHA sha1 numkeys [key [key ...]] [arg [arg ...]]Execute a cached script by its SHA1 checksum. Returns a NOSCRIPT error if the script is not in the cache—reload it with EVAL or SCRIPT LOAD, then retry.
EVALSHA_ROEVALSHA_RO sha1 numkeys [key [key ...]] [arg [arg ...]]Execute a cached script in read-only mode. Same behavior as EVALSHA with the write restriction of EVAL_RO.
SCRIPT LOADSCRIPT LOAD scriptLoad a script into the cache and return its SHA1 checksum. Use the checksum with EVALSHA for subsequent calls.
SCRIPT EXISTSSCRIPT EXISTS sha1 [sha1 ...]Check whether scripts are cached. Returns 1 for each cached script, 0 for each missing script.
SCRIPT KILLSCRIPT KILLTerminate the currently executing script. Orca automatically rolls back any data changes made by the script, including write scripts.
SCRIPT FLUSHSCRIPT FLUSH [ASYNC | SYNC]Clear all cached Lua scripts from the cluster. SYNC (default) blocks until complete; ASYNC clears the cache in the background.

Unsupported commands

The following commands cannot be called inside a Lua script using redis.call() or redis.pcall():

  • Transaction control: WATCH, UNWATCH, MULTI, EXEC, DISCARD

  • Blocking commands: BLPOP, BRPOP

  • Lua scripting commands: EVAL, EVAL_RO, EVALSHA, EVALSHA_RO, SCRIPT

  • Pub/Sub commands: SUBSCRIBE, UNSUBSCRIBE, PSUBSCRIBE, PUNSUBSCRIBE

  • Connection management: AUTH, HELLO, CLIENT

Calling any of these returns an error immediately.

Quick start

The following examples use the test key set by SET polardb orca.

EVAL

EVAL — Execute a script directly:

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

Output:

"orca"

SCRIPT LOAD

SCRIPT LOAD — Cache a script and get its SHA1 checksum:

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

Output:

"d3c21d0c2b9ca22f82737626a27bcaf5d288f99f"

EVALSHA

EVALSHA — Run the cached script using its SHA1 checksum:

EVALSHA d3c21d0c2b9ca22f82737626a27bcaf5d288f99f 1 polardb

Output:

"orca"

SCRIPT EXISTS

SCRIPT EXISTS — Check whether scripts are in the cache (first SHA1 exists, second does not):

SCRIPT EXISTS d3c21d0c2b9ca22f82737626a27bcaf5d288f99f ffffffffffffffffffffffffffffffffffffffff

Output:

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

Common scenarios

Implement an atomic counter

Use Lua scripting to avoid race conditions from concurrent INCR operations. The SCRIPT LOAD + EVALSHA pattern keeps the script content off the wire for subsequent calls, reducing network traffic and improving throughput.

1. Write the script and save it locally:

-- counter.lua
-- Initialize the key to 0 if it does not exist, then increment by 1.
if redis.call('EXISTS', KEYS[1]) == 0 then
  redis.call('SET', KEYS[1], 0)
end
return redis.call('INCR', KEYS[1])

2. Load the script to get its SHA1 checksum:

SCRIPT LOAD "if redis.call('EXISTS', KEYS[1]) == 0 then redis.call('SET', KEYS[1], 0) end return redis.call('INCR', KEYS[1])"

Output:

"b547eabbcde73b25330442e4f4e4dc1783b91241"

3. Execute the script using its SHA1 checksum:

EVALSHA b547eabbcde73b25330442e4f4e4dc1783b91241 1 my_counter

Output on first call:

(integer) 1

Output on second call:

(integer) 2

Run a read-only script

Use EVAL_RO or EVALSHA_RO to guarantee that a script does not perform write operations. This is useful for read/write splitting scenarios where scripts must only touch read replicas.

Before using EVAL_RO, confirm your script calls no write commands such as SET, DEL, HSET, or LPUSH. If it does, use EVAL instead.

EVAL_RO "return {redis.call('GET', KEYS[1]), redis.call('TTL', KEYS[1])}" 1 polardb

Output:

1) "orca"
2) (integer) -1
Note

If the script contains write commands, EVAL_RO returns (error) ERR Write commands are not allowed from read-only scripts.

Stop a long-running script

If a script runs longer than expected and blocks the cluster, terminate it with SCRIPT KILL. Orca automatically rolls back all changes the script has made.

SCRIPT KILL

Output:

OK
Note

SCRIPT KILL terminates only the currently executing script. You cannot target a specific script by SHA1.

Best practices

Use SCRIPT LOAD + EVALSHA for production workloads

All examples below use SCRIPT LOAD at startup to cache scripts, then EVALSHA for every subsequent call. This pattern avoids retransmitting the full script on each request.

1. Load the script during application initialization:

# Cache a script that sets a key's value
SCRIPT LOAD "return redis.call('set', KEYS[1], ARGV[1])"

Output:

"55b22c0d0cedf3866879ce7c854970626dcef0c3"

Store this SHA1 value in your application.

2. Execute the script using the cached SHA1:

# Set k1 = v1
EVALSHA 55b22c0d0cedf3866879ce7c854970626dcef0c3 1 k1 v1

# Set k2 = v2 using the same cached script
EVALSHA 55b22c0d0cedf3866879ce7c854970626dcef0c3 1 k2 v2

3. Handle `NOSCRIPT` errors:

If EVALSHA returns a NOSCRIPT error, the script is no longer in the cache. Switch to EVAL or SCRIPT LOAD to re-execute and re-cache the script, then resume using EVALSHA.

Parameterize scripts with KEYS[] and ARGV[]

Pass all variable data through KEYS[] and ARGV[] instead of hard-coding values in the script body. Hard-coded scripts are unique strings—each variation generates a separate cache entry, which inflates memory usage and defeats caching.

The following example shows the anti-pattern and the correct approach:

-- Anti-pattern: each hard-coded variant creates a separate cache entry
EVAL "return 'Hello'" 0
EVAL "return 'Scripting!'" 0

-- Correct: one parameterized script reuses the same cache entry for any value
EVAL "return ARGV[1]" 0 Hello
EVAL "return ARGV[1]" 0 Scripting!

Keep scripts short and fast

Lua scripts execute atomically and block all other commands during execution. Long-running or large-batch scripts increase cluster latency for every other client.

  • Avoid complex loops or large-scale data traversals inside scripts.

  • The default execution timeout is 300 seconds. A script that exceeds this limit is a signal to simplify the logic.

  • Split complex operations into multiple smaller scripts instead of one large one.

Clear the script cache safely

Use SCRIPT FLUSH to remove all cached scripts. After flushing, any call to EVALSHA or EVALSHA_RO returns a NOSCRIPT error until scripts are reloaded.

Warning

On clusters with many cached scripts, SCRIPT FLUSH may block the cluster for an extended period. Run this command during off-peak hours.

SCRIPT FLUSH

Output:

OK

Keep script source code in your application and implement automatic recovery for NOSCRIPT errors. On error, reload the script with SCRIPT LOAD or re-register it with EVAL.

FAQ

How do I fix `NOSCRIPT No matching script. Please use EVAL.`?

The script is no longer in the cluster cache. This happens after a cluster restart, an HA switchover, or a SCRIPT FLUSH call. In your client, catch NOSCRIPT errors and fall back to EVAL or SCRIPT LOAD to re-register the script. After re-registration, EVALSHA calls work again as expected.

What should I do when a script times out?

The default timeout is 300 seconds. A timeout usually means the script logic is too complex or processes too much data. To recover, run SCRIPT KILL—PolarDB rolls back all changes the script made. To prevent recurrence, optimize the script to avoid inefficient loops or large-scale data reads, or split the logic into multiple smaller scripts.

Why does `EVAL_RO` return `ERR Write commands are not allowed from read-only scripts.`?

EVAL_RO and EVALSHA_RO prohibit all write operations. Check the script for write commands such as SET, HSET, or DEL and remove them. If the script needs to write data, use EVAL or EVALSHA instead.