Why Redis Handles Millions of Requests: Deep Dive into Its High‑Performance Architecture
This article explains how Redis achieves ultra‑high concurrency by combining a single‑threaded event‑driven core, highly optimized in‑memory data structures, efficient I/O multiplexing, smart memory management, asynchronous persistence, and scalable clustering, allowing it to comfortably serve hundreds of thousands of simultaneous requests.
Redis High Concurrency Core Architecture
1.1 Single‑Thread Model
Redis runs network I/O and command processing in a single thread, which eliminates context‑switch overhead, lock contention, and improves CPU‑cache locality.
Avoids thread‑switch overhead : no need to save and restore thread contexts.
Avoids lock competition : single thread removes the need for mutexes.
CPU‑cache friendly : continuous execution keeps data hot in cache.
// Multi‑thread pseudo code
public class MultiThreadRedis {
private final Object lock = new Object();
private Map<String, String> data = new HashMap<>();
public String get(String key) {
synchronized (lock) {
return data.get(key);
}
}
public void set(String key, String value) {
synchronized (lock) {
data.put(key, value);
}
}
}
// Redis single‑thread pseudo code
public class SingleThreadRedis {
private Map<String, String> data = new HashMap<>();
public String get(String key) {
return data.get(key); // no lock
}
public void set(String key, String value) {
data.put(key, value); // no lock
}
}1.2 Event‑Driven Model
Redis uses a Reactor‑style event loop where one thread multiplexes I/O for many connections.
Efficient I/O multiplexing (epoll/kqueue/select)
Non‑blocking I/O prevents a single slow socket from stalling the server
Low memory footprint compared to thread‑per‑connection models
2. Memory Data‑Structure Optimizations
2.1 Efficient Data‑Structure Design
Redis chooses the most suitable internal representation for each data type, automatically switching based on size and content.
2.1.1 SDS (Simple Dynamic String)
Redis implements its own string type to store length, free space, and buffer in a contiguous block.
struct sdshdr {
int len; // string length
int free; // unused space
char buf[]; // actual bytes
};O(1) length access : read the len field directly
Pre‑allocation : reduces reallocations
Binary safe : can store any byte sequence
C‑string compatible : null‑terminated for library calls
2.1.2 Skip List
Core of sorted sets, providing average O(log N) search, insertion, and deletion.
// Skip‑list search pseudo code
public Node search(int target) {
Node current = header;
for (int level = maxLevel; level >= 0; level--) {
while (current.forward[level] != null && current.forward[level].value < target) {
current = current.forward[level];
}
}
current = current.forward[0];
if (current != null && current.value == target) return current;
return null;
}2.2 Memory‑Optimization Strategies
2.2.1 Ziplist
Compact sequential encoding used for small hashes, lists, and sorted sets.
Memory‑compact: elements stored contiguously
Cache‑friendly: sequential access improves CPU cache hit rate
Saves pointer overhead by eliminating per‑element pointers
2.2.2 Intset
Optimized integer set used when a set contains only numbers.
typedef struct intset {
uint32_t encoding; // 16/32/64 bit
uint32_t length; // number of elements
int8_t contents[]; // packed integers
} intset;3. Network I/O Optimizations
3.1 I/O Multiplexing Techniques
Different operating systems use the most efficient mechanism:
Linux: epoll
macOS/FreeBSD: kqueue
Windows: select
3.2 Client Output Buffers
Each client has a dedicated output buffer to isolate slow consumers.
# redis.conf example
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit replica 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 604. Memory Management Optimizations
4.1 Allocator Choice
Redis defaults to jemalloc , a allocator tuned for multithreaded workloads and low fragmentation.
4.2 Expired‑Key Deletion Strategy
Redis combines lazy deletion with a periodic active‑expire cycle to reclaim memory without blocking the main thread.
public void activeExpireCycle() {
int maxIterations = 16;
int maxChecks = 20;
for (int i = 0; i < maxIterations; i++) {
RedisDb db = server.db[i];
int expired = 0;
for (int j = 0; j < maxChecks; j++) {
String key = db.expires.randomKey();
if (key != null && isExpired(key)) {
deleteKey(key);
expired++;
}
}
if (expired < maxChecks / 4) break;
}
}5. Persistence Optimizations
5.1 RDB Persistence
Snapshotting at configured intervals produces a compact binary file, allowing fast restarts and minimal impact on the server because it runs in a child process.
5.2 AOF Persistence
The Append‑Only File logs every write command; periodic rewriting compacts the log by removing redundant operations.
// AOF rewrite pseudo code
public void rewriteAOF() {
for (RedisDb db : server.databases) {
for (String key : db.dict.keys()) {
Object value = db.dict.get(key);
generateCommand(key, value);
}
}
}6. Cluster and Sharding Optimizations
6.1 Redis Cluster
Redis Cluster provides a decentralized sharding solution with 16384 hash slots, automatically distributing keys across multiple nodes.
public int calculateSlot(String key) {
int start = key.indexOf('{');
if (start != -1) {
int end = key.indexOf('}', start + 1);
if (end != -1 && end != start + 1) {
key = key.substring(start + 1, end);
}
}
int crc = crc16(key.getBytes());
return crc % 16384;
}6.2 Sharding Strategies
Redis offers multiple sharding methods to avoid data skew and balance load across nodes.
7. Performance Monitoring and Tuning
7.1 Key Metrics
Useful commands: INFO , MONITOR , SLOWLOG , CLIENT LIST , MEMORY USAGE , LATENCY .
7.2 Tuning Recommendations
Memory settings (hash‑max‑ziplist‑entries, list‑max‑ziplist‑size, etc.), eviction policy (allkeys‑lru), TCP tweaks (tcp‑keepalive, tcp‑backlog), and output‑buffer limits improve throughput.
# Example memory tuning
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-size -2
list-compress-depth 0
set-max-intset-entries 512
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
maxmemory-policy allkeys-lru
rdbcompression yes8. Fault Tolerance and High Availability
8.1 Failure Detection
Redis nodes exchange heartbeat messages to detect failures quickly.
8.2 Data Consistency Guarantees
Master‑slave replication combines full resynchronization (BGSAVE) with incremental partial resynchronization using replication offsets.
public void fullResync() {
// 1. Slave sends PSYNC
// 2. Master runs BGSAVE to create RDB
// 3. Master streams RDB to slave
// 4. Slave loads RDB
// 5. Master sends buffered write commands
}
public void partialResync() {
// 1. Slave sends PSYNC with runid and offset
// 2. Master checks if offset is still in its replication backlog
// 3. If yes, streams missing commands only
}Conclusion
Redis achieves its ability to handle hundreds of thousands of concurrent requests through a combination of a single‑threaded event loop, highly tuned in‑memory data structures, efficient I/O multiplexing, asynchronous persistence mechanisms, and a scalable clustering architecture.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Su San Talks Tech
Su San, former staff at several leading tech companies, is a top creator on Juejin and a premium creator on CSDN, and runs the free coding practice site www.susan.net.cn.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
