Part 6 · 1 chapters · ~8 min
Lua, Functions, Transactions and Pipelining
Pipelining to cut round trips (measured), MULTI/EXEC transactions and what they do not guarantee, WATCH for optimistic locking, Lua scripts and Redis Functions for atomic logic, script costs and blocking, and choosing between them.
8
Atomic logic and fewer round trips
code
# pipelining: send many commands, read replies once (measured: GET 172k/s → 1.81M/s with -P 16)
# MULTI/EXEC: queued commands run together, no interleaving; but no rollback if one fails
MULTI
DECRBY wallet:7 500
INCRBY wallet:9 500
EXEC
# WATCH: optimistic check-and-set
WATCH wallet:7
GET wallet:7 # client checks the value...
MULTI
DECRBY wallet:7 500
EXEC # nil if wallet:7 changed after WATCH: retry
-- Lua: the check and the update atomically, in one round trip
-- EVAL script 1 wallet:7 500
local bal = tonumber(redis.call('GET', KEYS[1]) or '0')
if bal < tonumber(ARGV[1]) then return -1 end
return redis.call('DECRBY', KEYS[1], ARGV[1])| tool | atomic | conditional logic | use |
|---|---|---|---|
| pipeline | no | no | throughput for independent commands |
| MULTI/EXEC | yes, no interleaving | no (only WATCH-based retry) | a fixed set of writes together |
| Lua / Functions | yes | yes | check-then-act (rate limiters, locks, counters with limits) |
Scripts run on the single command thread: keep them short, since a slow script blocks every client. Redis Functions (7+) are named, persisted libraries of Lua code, loaded once instead of sent with each call. And remember: Redis is not your ledger; balances belong in a database with constraints (Ledgers course).