Atomic Multi‑Key Operations in Redis with Lua Scripting (EVAL)
Learn how Redis Lua scripts let you perform multiple key updates atomically in a single round‑trip, with a step‑by‑step example, limits, and common pitfalls to avoid.
14 Feb 2026, 15:43 UTC

Why Lua Scripting Matters for Atomicity
In many applications you need to update several keys in Redis without the risk of a race condition. Traditional commands such as INCR or SET are atomic on a single key, but when you combine them you normally need a transaction or a Lua script. Redis Lua scripts run inside the server process, guaranteeing that the sequence of calls you write is executed as one atomic block. No other client can see intermediate state, and the server never blocks other clients while the script runs (unless the script itself is long‑running).
How EVAL Executes a Script
The EVAL command has the following syntax:
EVAL <script> <numkeys> <key1> <key2> ... <arg1> <arg2> ...
Redis passes the keys after <numkeys> to the script as KEYS[1], KEYS[2], etc. Remaining values are available as ARGV[1], ARGV[2], and so on. The script is executed by the embedded Lua interpreter. If the script returns a value, that value is sent back to the client. If it throws an error, the client receives an error reply.
Worked Example: Counter + Flag
Suppose you want to increment a counter and set a flag only on the first increment. The logic is:
- Increment
counter. - If the new value is 1, set
flagto a supplied value. - Return the new counter value.
Lua script:
local val = redis.call('INCR', KEYS[1])
if val == 1 then
redis.call('SET', KEYS[2], ARGV[1])
end
return val
Call it from redis-cli:
redis-cli EVAL "local val = redis.call('INCR', KEYS[1]) if val == 1 then redis.call('SET', KEYS[2], ARGV[1]) end return val" 2 counter flag "done"
Run the command twice. The first run returns 1 and sets flag to done. The second run returns 2 and does not touch flag. All in one network round‑trip.
Configuration & Limits
Redis imposes several limits on scripts:
| Parameter | Default | Effect |
|---|---|---|
| max‑script‑size | 1 MB | Maximum size of the Lua script string. The limit can be increased up to 512 MB. |
| max‑script‑time | 0 ms (disabled) | Maximum execution time in milliseconds. A value of 0 disables the limit. If a script exceeds this time, Redis aborts it with a timeout error. |
| script cache | Enabled | Scripts are cached by their SHA1 hash. Subsequent EVALSHA calls are faster. |
Because Lua runs on a single CPU core, a long script will block all other commands. Avoid loops over large ranges or recursive calls that can run for seconds.
Common Pitfalls & How to Avoid Them
- Incorrect KEYS/ARGV usage – If you accidentally reference a key that isn’t in the
KEYSarray, Redis will treat it as a literal string, potentially exposing unintended keys. Always useKEYS[i]for keys andARGV[j]for arguments. - Ignoring the 512 MB size limit – Large scripts can be split into smaller functions or moved to client‑side logic. Keep scripts under 1 MB unless you explicitly need more.
- Writing blocking Lua code – Loops that iterate millions of times or perform heavy calculations can freeze the server. Test scripts in a staging environment and measure CPU usage.
- Uncaught script errors – A syntax error or runtime exception returns a generic error. Wrap critical calls in
pcallif you need graceful failure handling. - Assuming scripts run on multiple cores – All Lua scripts run on the same CPU core as the Redis process. Parallelism is not gained by scripting.
Testing & Verification
To confirm atomicity, run two clients concurrently that each call the script to increment the same counter. You should observe that the counter increments by one per script execution, never by two at once.
To test max‑script‑time, set max-script-time 1000 in redis.conf (or CONFIG SET max-script-time 1000), then run a script that sleeps or loops for more than a second. Redis will abort with a timeout error, which you can verify by checking the error message.
Always run scripts against a non‑production instance first. Use the SCRIPT LOAD command to cache the script and SCRIPT EXISTS to verify that the SHA1 hash is present.
Practical Takeaway
Use EVAL when you need to perform a set of Redis commands that must be atomic and you want to reduce network latency. Keep scripts small, avoid blocking operations, and leverage SCRIPT LOAD for production deployments. With these practices, you can safely perform complex multi‑key updates without race conditions or performance regressions.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.