Redis
Redis
Redis is an open-source in-memory data structure store, usable as:
- Database
- Cache
- Message broker
- Streaming engine
It provides data structures out of the box:
- hash, list, set, sorted set, bitmap, hyperloglog
- geospatial indexes, streams
These build a variety of applications:
- Real-time chat
- Message buffers
- Gaming leaderboards
- Auth/session store
- Media streaming
- Real-time analytics
What makes Redis special?
- Every operation is atomic — while a command executes, Redis does not context-switch to another command. Examples:
- Putting a key
- Adding to a list
- Set union / intersection
- Incrementing a value
- Data is stored in-memory → most common use is caching. Redis also offers configurable persistence:
- Periodically dumping data to disk (RDB snapshot)
- Write-ahead log of all commands (AOF)
- No persistence at all
- Other key features: Transactions, Pub/Sub, TTL on keys, LRU eviction.
Concurrent Programming Models (single process)
Concurrent programming = doing more than one thing at the same time.
Multi-threading
- Each incoming request is accepted by the server and executed in a separate thread.
- Example:
R1 → INCR K → T1R2 → INCR K → T2
- Ensuring data correctness: if
K = 10and two threads runK++, possible final values are 11 and 12 (lost update). Other threads must wait while one is in the critical section:ACQ LOCK K++ REL LOCK - Done via mutex / semaphores (pessimistic locking).
I/O Multiplexing (apparent concurrency)
The event loop uses this.
- I/O is blocking — e.g. reading from a socket:
read()blocks until the other side sends. - So you can't just invoke
read()on a socket unless you know data is coming. - Handling each connection on a separate thread is one solution (while one thread blocks, another runs) — but needs mutex/semaphores to protect critical sections.
- Better question: can we not wait on I/O — get notified when there's movement?
- That is I/O multiplexing.
- Core idea: use I/O monitoring syscalls (
select/poll/epoll/kqueue) to watch many sockets, and firereadonly on the ones that have data.
![[Pasted image 20260628140849.png]]
What Redis exploits to be fast
- Network I/O is slow → waiting to receive commands.
- In-memory ops are fast → once a command arrives, Redis executes it quickly.
- So Redis made two conscious design choices:
- Single-threaded → no mutexes/semaphores, no lock waiting.
- I/O multiplexing → handle many TCP connections concurrently on that one thread.
Talking the Redis Language — RESP
- To understand a client, the server needs a protocol: Redis Serialisation Protocol (RESP).
- RESP supports common datatypes (int, string, arrays) and a way to convey errors.
- Redis sends a command as an array of strings:
PUT K V→["PUT", "K", "V"]→ serialised with RESP.
RESP description
- RESP is a request–response protocol: client sends request in RESP, server responds in RESP.
- Every datatype starts with a special character and ends with
\r\n(CRLF).
| Type | Prefix | Example |
|---|---|---|
| Simple String | + | +PONG\r\n |
| Integer | : | :1729\r\n |
| Bulk String | $ | $4\r\nPONG\r\n |
| Array | * | *2\r\n$4\r\nLLEN\r\n$6\r\nmylist\r\n |
| Error | - | -Key not found\r\n |
Simple Strings
- Start with
+, then the string, then CRLF. - Minimal overhead:
n + 3bytes. - Eg.
+PING\r\n,+PONG\r\n.
Integers
- Start with
:, then the integer, then CRLF. Eg.:1729\r\n.
Bulk Strings
- Start with
$, then byte count, CRLF, the actual string, CRLF. Eg.$4\r\nPONG\r\n. - Why bulk strings if we have simple strings?
- Simple strings are not binary-safe (can't contain arbitrary bytes).
- Simple strings cannot contain
\r\n(it's the terminator). - Bulk strings can store any binary data — even a PNG.
- Special forms:
- Empty string:
$0\r\n\r\n - Null value:
$-1\r\n(length-1signals absence of data)
- Empty string:
Arrays
- Start with
*, then element count, CRLF, then RESP-encoded elements. - Eg.
["a", 200, "cat"]→*3\r\n$1\r\na\r\n:200\r\n$3\r\ncat\r\n(no spaces). - Null array:
*-1\r\n· Empty array:*0\r\n· Nesting allowed.
Errors
- Start with
-, then message, then CRLF. Eg.-Key not found\r\n.
Why RESP over JSON
JSON is inefficient — needs parsing, quote management, gets bulky. RESP is:
- Human-readable
- Simple
- Performant
- Prefixed-length → you know exactly how many bytes to read.
TODO — Build Your Own Redis (hands-on)
Implementation project + per-stage guide (Python & Go): [[build-your-own-redis/README]]
- Stage 1 — Simple TCP server, 1 concurrent connection
- Stage 2 — RESP encoding/decoding
- Stage 3 — PING → PONG
- Stage 4 — I/O multiplexing for many connections
- Stage 5 — Command dispatch +
SET/GET - Stage 6 — TTL / expiry
Interview Trade-Offs (Redis)
- Single-threaded + I/O multiplexing > thread-per-connection for an in-memory store: no lock contention, cache-friendly, and CPU is never the bottleneck (network is). Trade-off: one slow command (
KEYS *, bigSORT) blocks everything → avoid O(n) commands in hot paths. - RESP vs JSON/HTTP: prefixed-length binary-safe framing beats delimiter-parsing for a high-throughput protocol.
- Persistence: RDB (compact, fast restart, risk losing last window) vs AOF (durable, replayable, larger/slower) — often run both.
Related
- [[synthesis/DDIA Interview Map]] · [[synthesis/Job Switch Hub]]
- Redis playlist arc S1–S7: [[Coach/project_dsa_sd_curriculum]]