Redis Overview
Redis is an in-memory data structure server. The important design move is not "put data in Redis", it is choosing the data structure and command shape that matches the production job.
Where Redis Fits
Redis usually sits beside the source-of-truth database, not instead of it. PostgreSQL owns relational truth and durable invariants. Redis owns fast access paths, shared counters, temporary coordination, fanout, and lightweight event buffers.
Latency tool
Use Redis when repeated reads, counters, or coordination checks must avoid a slower database round trip.
Data structure tool
Use Redis when the primitive itself is the feature: set membership, sorted ranking, list pop, stream append, or atomic increment.
Failure tradeoff
Redis buys speed by making memory, expiry, eviction, persistence, and replica lag part of the application design.
How A Command Works
Clients talk to Redis with RESP, the Redis Serialization Protocol. RESP is a request-response wire format where clients send commands as arrays of bulk strings, and Redis returns command-specific replies. Pipelining lets clients send multiple commands before waiting for each response, reducing round trips.
Select a component or arrow
Boxes are Redis runtime parts. Arrows are movement of requests, key lookups, replies, persistence, or replication. Select any part to focus the related path.
INCR rl:user:42 travels over TCP as RESP.
Redis parses command name and arguments.
The command mutates or reads an in-memory key.
TTL, AOF, replication, and memory policy may be touched.
Common RESP replies include integers, strings, arrays, nulls, and errors. RESP3 adds more reply types.
RESP Command Anatomy
The command SET name aniket is sent as an array of three bulk strings: command name, key, and value.
*3\r\n
$3\r\n
SET\r\n
$4\r\n
name\r\n
$6\r\n
aniket\r\n
*3 means the outer object is an array of three items. Each $N line says the next bulk string has N bytes. The text form is enough here: RESP is intentionally simple.
Core Internals
| Part | What it does | Why it matters |
|---|---|---|
| Keyspace | Top-level namespace mapping string keys to typed values. | The key name is the routing and lifecycle unit. TTL, deletion, and cluster placement all hang off keys. |
| Native data structures | Strings, hashes, lists, sets, sorted sets, streams, and other types. | Redis commands operate directly on structures, so the modeling choice decides both correctness and performance. |
| Atomic commands | Single commands run as indivisible operations from the perspective of other clients. | INCR, SET NX PX, HINCRBY, and XADD avoid client-side read-modify-write races. |
| Transactions | MULTI queues commands and EXEC runs them as one isolated batch. WATCH adds optimistic check-and-set. |
Useful when commands must be grouped. Redis transactions are not SQL transactions: executed command errors do not roll the batch back. |
| Expiry | Keys can have TTLs through commands such as EXPIRE, SET EX, and SET PX. |
Expiry turns Redis into a temporary truth layer: cache entries, rate windows, sessions, and leases can disappear by design. |
| Persistence | RDB snapshots capture point-in-time state. AOF logs write operations with configurable fsync policy. | Persistence reduces restart loss, but Redis is still usually designed as memory-first infrastructure. |
| Eviction | maxmemory and maxmemory-policy decide what happens when Redis exceeds memory budget. |
A cache may safely evict. A lock, session, or queue may not be safe to evict without application consequences. |
| Replication and failover | Redis can replicate writes to replicas and use Sentinel or Cluster patterns for availability and scaling. | Replicas help reads and recovery, but asynchronous replication means recent writes can be lost during failover. |
What Data Can Be Stored
Redis keys are strings. Values are typed data structures. A key cannot be both a string and a hash at the same time. A wrong command against the wrong type returns a type error.
| Type | Shape | Good for | Example commands |
|---|---|---|---|
| String | Binary-safe bytes, integers as strings, JSON blobs, tokens. | Cache values, counters, feature flags, idempotency markers, locks. | GET, SET, INCR, MGET |
| Hash | Field-value map under one key. | Small objects such as session fields, user profile fragments, payment status fields. | HSET, HGET, HINCRBY |
| List | Insertion-ordered sequence. | Simple queues, recent activity, bounded history. | LPUSH, RPOP, BLPOP |
| Set | Unordered unique members. | Membership, deduplication, online users, permissions, tags. | SADD, SISMEMBER, SINTER |
| Sorted set | Unique members ordered by score. | Leaderboards, delayed jobs, priority queues, time-window indexes. | ZADD, ZRANGE, ZPOPMIN |
| Stream | Append-only log with IDs and fields. | Event buffers, consumer groups, persisted work delivery when Redis persistence, trimming, and failover settings match the requirement. | XADD, XREAD, XGROUP, XACK |
| Bitmap | Bit-level operations over a string. | Daily active users, feature bitsets, compact boolean flags. | SETBIT, GETBIT, BITCOUNT |
| HyperLogLog | Probabilistic cardinality estimate. | Approximate unique visitors or unique devices with tiny memory. | PFADD, PFCOUNT |
| Geospatial | Longitude and latitude indexed through sorted-set mechanics. | Nearby stores, drivers, ATMs, or delivery zones. | GEOADD, GEOSEARCH |
| JSON, time series, vector sets, search | Structured document, metric, vector, and query features in current Redis distributions. | Document caching, search indexes, metrics, vector similarity, AI retrieval paths. | JSON.SET, FT.SEARCH, TS.ADD, VADD |
Use Cases
The same Redis server can power many patterns, but each pattern has a different correctness story. Choose by asking: what is the source of truth, what can expire, what can be lost, and what must be atomic?
Cache-aside
Redis holds a copy of data whose durable home is somewhere else. Misses go to the database, then populate Redis with a TTL.
Risk: stale data, thundering herd on miss, and unsafe assumptions if Redis becomes the only copy.
GET product:42
SET product:42 "{\"stock\": 99}" EX 60
TTL product:42
Use Case Decision Rules
| Use Redis for | Good design | Bad smell |
|---|---|---|
| Cache | Database remains source of truth. Cache key has clear invalidation or TTL. | Business-critical value exists only in Redis with eviction enabled. |
| Rate limiter | Use atomic increments plus expiry. Keep window semantics explicit. | Separate GET, app math, and SET under concurrency. |
| Lock or lease | Use unique ownership token, SET NX PX, and token-checked release. |
Plain DEL lock without checking owner, or lock TTL longer than failure detection. |
| Queue | Use streams and consumer groups when ack and retry matter. | Using Pub/Sub for work that must survive subscriber disconnects. |
| Ranking | Sorted set score is the ordering rule. Ties and updates are understood. | Trying to run relational joins or arbitrary filtering inside Redis. |
| Dedup | Set or string marker has a TTL aligned with replay window. | Marker expires before upstream retries can arrive. |
Hands-On Redis Labs
The lab pages explain the exact Redis commands, key shapes, Lua scripts, terminal output, and production gotchas behind each runnable experiment.
Cache stampede
Why many workers call the backend after the same key expires, and how a short fill lock reduces the herd.
Rate limiting
Fixed-window boundary bursts, sorted-set sliding windows, and why sliding windows are not fully smooth.
Locks
SET NX PX, ownership tokens, and Lua compare-and-delete for safe release.
Streams
Consumer groups, pending entries, crash recovery, XPENDING, and XAUTOCLAIM.
Pub/Sub
Fire-and-forget fan-out compared with stream-backed replay and acknowledgement.
Lua And Lua Scripts
Lua is a small embedded programming language. Redis embeds Lua 5.1 so clients can send scripts with EVAL or load them and call by hash. Scripts run on the Redis server, next to the data.
Lua runs beside the data
The client sends one script call. Redis executes the reads, checks, writes, and return value as one uninterrupted operation, so no other client sees the middle state.
When Lua is useful
- Conditional update: decrement stock only if enough quota remains.
- Safe lock release: delete a lock only if its value matches your owner token.
- Rate limiter: increment a counter and set expiry only when the key is new.
- Queue bookkeeping: move a job between structures without a visible half-state.
- Cross-key checks on one Redis node when a single built-in command is not enough.
Safe lock release script
EVAL "
if redis.call('GET', KEYS[1]) == ARGV[1] then
return redis.call('DEL', KEYS[1])
else
return 0
end
" 1 lock:payment:123 worker-a-uuid
This prevents one worker from deleting another worker's renewed or re-acquired lock. The key name is passed through KEYS. The owner token is passed through ARGV. That matters because Redis Cluster needs scripts to declare their key access clearly.
Fixed-window rate limiter script
EVAL "
local current = redis.call('INCR', KEYS[1])
if current == 1 then
redis.call('EXPIRE', KEYS[1], ARGV[1])
end
return current
" 1 rl:user:42 60
The script makes the first increment and expiry setup one atomic operation. Without the script, a client crash between INCR and EXPIRE can leave a counter that never resets.
Lua cautions
- Scripts block Redis while they execute, so keep them short.
- Pass keys through
KEYSand non-key arguments throughARGV. - Do not generate a unique script body for every request. Parameterize scripts instead.
- For long-lived server-side logic in Redis 7+, evaluate Redis Functions as an alternative.
Persistence, Eviction, And Durability
Redis can persist data, but persistence choice changes what failures mean.
| Mechanism | How it works | Tradeoff |
|---|---|---|
| RDB snapshot | Periodically writes a compact point-in-time snapshot. | Fast restart file, but recent writes after the last snapshot can be lost. |
| AOF | Appends write operations to a log and rewrites it in the background when needed. | More durable with fsync policy, but larger files and possible write overhead. |
| No persistence | Data lives only in memory. | Fine for disposable cache, dangerous for source-of-truth state. |
| Eviction | When maxmemory is exceeded, Redis follows the configured eviction policy. |
Safe for cache copies. Risky for locks, streams, sessions, and idempotency markers. |
Redis Versus PostgreSQL
| Question | PostgreSQL answer | Redis answer |
|---|---|---|
| What is the source of truth? | Usually yes. Relational constraints, transactions, durable commits. | Only when explicitly designed that way. Often a derived or temporary view. |
| How do I protect invariants? | Transactions, row locks, constraints, isolation levels, atomic SQL. | Atomic commands, Lua scripts, optimistic WATCH, careful key modeling. |
| What happens under memory pressure? | Queries slow, disk and buffer behavior matter. | Keys may be evicted or writes may fail, depending on policy. |
| What is the modeling unit? | Tables, rows, indexes, constraints, joins. | Keys and command-specific data structures. |
| What is the common mistake? | Assuming read-modify-write is safe without the right lock or isolation. | Assuming speed implies durability, or that TTL/eviction cannot affect correctness. |
Practical First CLI Experiments
These are enough to learn the shape before writing application code.
# cache with TTL
SET product:42 "{\"price\": 1999}" EX 30
GET product:42
TTL product:42
# rate counter
INCR rl:user:42
EXPIRE rl:user:42 60
TTL rl:user:42
# lock acquisition
SET lock:payment:123 worker-a NX PX 5000
GET lock:payment:123
# stream event
XADD payments * id p1 amount 100
XREAD COUNT 1 STREAMS payments 0
# sorted ranking
ZADD leaderboard 900 user:42 850 user:7
ZREVRANGE leaderboard 0 9 WITHSCORES