Reading — step 1 of 3
Server-Side Scripting
EVAL — Server-Side Lua Scripting
Redis embeds a Lua interpreter. You ship a script, Redis runs it atomically, and you get back the result. This is Redis's most powerful primitive — every other command can be composed inside a script.
The Two Killer Use Cases
1. Atomic compound operations. Without scripts you'd need WATCH/MULTI/EXEC and a retry loop. With EVAL, a 5-step read-modify-write happens atomically with zero round trips.
2. Reducing network round-trips. Loops that fire 100 Redis commands become a single EVAL.
The Wire Format
EVAL <script> <numkeys> <key1>...<keyN> <arg1>...<argM>
script— the Lua source codenumkeys— how many of the following args are keys- The split between keys and args matters for Redis Cluster routing (keys must hash to the same slot)
Inside the script, two globals are bound:
KEYS— an array of the keys (1-indexed, Lua style)ARGV— an array of the other args
redis.call
Inside Lua, redis.call(cmd, arg1, ...) invokes a Redis command. It returns the result as a Lua value, type-mapped:
| Redis type | Lua type |
|---|---|
Integer (:N) | number |
Simple string (+OK) | table {ok = "OK"} |
Bulk string ($len) | string |
Null bulk ($-1) | false |
Array (*N) | table (1-indexed) |
Error (-ERR) | raises Lua error |
When Lua returns a value, Redis maps it back to a RESP type:
| Lua return | RESP output |
|---|---|
number | Integer :N\r\n |
string | Bulk string $len\r\n...\r\n |
nil / false | Null $-1\r\n |
{ok = "X"} | Simple string +X\r\n |
{err = "msg"} | Error -msg\r\n |
| array table | Array *N\r\n... |
A Real Atomic INCR-with-Cap
This is the canonical example — capped counter, atomic:
local v = tonumber(redis.call('GET', KEYS[1])) or 0
if v >= tonumber(ARGV[1]) then return 0 end
return redis.call('INCR', KEYS[1])
Five operations, zero races, one round trip. Try expressing that with WATCH/MULTI/EXEC — it's a retry loop. With EVAL it's straight-line code.
EVALSHA — Script Caching
Sending the script every time is wasteful. SCRIPT LOAD <script> returns the SHA-1 of the script and caches it server-side. Then EVALSHA <sha1> <numkeys> ... runs the cached script. Real clients automatically do EVALSHA-then-fall-back-to-EVAL.
What You'll Build
A small dispatcher that recognizes a handful of common script shapes and routes them to your existing handlers. You don't need a full Lua interpreter — you need to understand the contract between scripts and the command runtime.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…