Lua Scripting
Redis includes a built-in Lua interpreter that allows you to run server-side scripts using the command:
EVAL <script> <numkeys> key1 key2 arg1 arg2 ...
Why Lua Scripting Matters for Performance
When you execute multiple Redis commands from your application:
Without optimization
Your app → Redis → app → Redis (many round trips)
This creates:
- high latency
- many network packets
- slow client-side logic
With Lua scripting
Your app → Redis (one script) → Redis runs everything internally
Huge performance improvements because:
- 0 network round trips inside script
- Atomicity guaranteed
Atomic Increment with Logic
Suppose you want: Increment a counter only if a limit is not exceeded.
Without Lua (multiple round trips):
GET counter- compute in app
SET counter newValue
This can cause race conditions in concurrent environments.
EVAL "
local current = redis.call('GET', KEYS[1])
if not current then
redis.call('SET', KEYS[1], 1)
return 1
elseif tonumber(current) < tonumber(ARGV[1]) then
redis.call('INCR', KEYS[1])
return redis.call('GET', KEYS[1])
else
return current
end
" 1 counter 10
KEYS[1]="counter"ARGV[1]=10(limit)- Logic:
- If counter doesn't exist → set to 1
- If under limit → increment
- If over limit → return current value
- All done atomically and in one server-side script
Why Lua is Faster Than Multi-Command Client Logic
Because Redis runs Lua scripts without releasing the CPU until finished.
This makes it:
- Safer (no race conditions)
- Faster (no network calls)
- More powerful (conditional logic on server)
Lua Scripting vs MULTI/EXEC
| Feature | Lua Scripting | MULTI/EXEC |
|---|---|---|
| Atomic? | ✔ Entire script atomic | ✔ Entire transaction atomic |
| Conditional logic? | ✔ Fully flexible | ❌ No logic inside transaction (client must compute) |
| Network round trips | ✔ 1 round trip | ❌ Multiple (commands are queued) |
| Performance | ⭐ Fastest for multi-ops | ⚡ Fast |
| Error behavior | Script runs fully or errors early | Errors only on EXEC |
| Server-side loops? | ✔ Yes | ❌ No |
| Ideal for | complex logic, repeated patterns | simple atomic sequences |
Lua Scripting vs Pipelining
Pipelining sends multiple commands in one batch but:
| Feature | Lua Scripting | Pipelining |
|---|---|---|
| Atomic | ✔ Yes | ❌ No |
| Conditional logic | ✔ Yes | ❌ No |
| Network round trips | ✔ 1 | ✔ 1 |
| Executes server-side? | ✔ Yes | ❌ No (just client batching) |
| Order guarantee | ✔ Yes | ✔ Yes |
| Ideal for | stateful logic | high-volume simple commands |
Compare Lua, MULTI/EXEC, and Pipelining
Requirement: Increment three keys and return their sum
Approach 1: Without Optimization (3 round trips)
INCR k1
INCR k2
INCR k3
- 3 network round trips
- 3 server executions
- client computes sum
Approach 2: Pipelining (1 round trip)
INCR k1
INCR k2
INCR k3
Sent as a pipeline → 1 round trip
BUT:
- Still 3 server operations
- No atomicity
- Client computes last step
Approach 3: MULTI/EXEC (Atomic but more round trips)
MULTI
INCR k1
INCR k2
INCR k3
EXEC
- ✔ atomic
- ❌ 2 network round trips (MULTI + EXEC)
- ❌ cannot compute sum server-side
Approach 4: Lua Scripting (Best)
EVAL "
local a = redis.call('INCR', KEYS[1])
local b = redis.call('INCR', KEYS[2])
local c = redis.call('INCR', KEYS[3])
return a + b + c
" 3 k1 k2 k3
- 1 round trip
- All increments occur inside Redis
- Sum computed server-side
- Atomic
- Fastest option
Between Lua, MULTI/EXEC, and Pipelining
| If you need… | Use |
|---|---|
| Complex logic | Lua |
| Atomicity + logic | Lua |
| Many simple commands with no logic | Pipelining |
| Strict atomic multi-command bundle | MULTI/EXEC |
| Server-side computation (loops, conditionals) | Lua |
| Minimizing network latency | Lua or Pipelining |