Redis Network Model Analysis
Detailed analysis on the source code of redis network model
Deep dive into how Redis achieves exceptional performance through its network
architecture:
Event-Driven Architecture:
Single-Threaded Event Loop:
- Main Thread: All Redis operations run in a single thread
- Non-blocking I/O: Uses epoll/kqueue for efficient I/O multiplexing
- Event Processing: Commands processed sequentially without context
switching
- Atomicity: Single-threaded nature ensures atomic operations
Event Types:
1
2
3
4
5
6
7
8
9
10
11
| // File events for network I/O
#define AE_READABLE 1 // Socket ready for reading
#define AE_WRITABLE 2 // Socket ready for writing
// Time events for periodic tasks
typedef struct aeTimeEvent {
long long id; // Time event identifier
long when_sec; // When to fire (seconds)
long when_ms; // When to fire (milliseconds)
aeTimeProc *timeProc; // Time event handler
} aeTimeEvent;
|
Network I/O Implementation:
Client Connection Handling:
- Accept Phase: New client connections accepted
- Read Phase: Commands read from client sockets
- Process Phase: Commands executed in Redis core
- Write Phase: Responses written back to clients
- Cleanup Phase: Closed connections handled
Buffer Management:
1
2
3
4
5
6
7
| typedef struct client {
int fd; // Client socket file descriptor
sds querybuf; // Input buffer for commands
list *reply; // Output buffer list
unsigned long reply_bytes; // Bytes in output buffer
size_t querybuf_peak; // Peak query buffer size
} client;
|
Pipeline Support:
- Batch Processing: Multiple commands in single network round-trip
- Reduced Latency: Fewer network calls for better performance
- Buffer Reuse: Efficient memory management for pipelined requests
Memory Efficiency:
- Zero-Copy: Direct buffer manipulation where possible
- Pooled Buffers: Reuse of network buffers
- Lazy Deletion: Deferred cleanup of large objects
Redis Under the Hood
Redis: under the hood
Comprehensive exploration of Redis internal data structures and algorithms:
Core Data Structures:
String Implementation:
1
2
3
4
5
| struct sdshdr {
int len; // String length
int free; // Available space
char buf[]; // Actual string data
};
|
Features:
- Dynamic Sizing: Automatic growth and shrinkage
- Binary Safe: Can store any byte sequence
- Memory Efficient: Minimal overhead compared to C strings
- O(1) Length: Length stored, not calculated
Hash Table (Dict):
1
2
3
4
5
6
| typedef struct dict {
dictType *type; // Type-specific functions
void *privdata; // Private data
dictht ht[2]; // Two hash tables for rehashing
int rehashidx; // Rehashing progress (-1 if not rehashing)
} dict;
|
Rehashing Strategy:
- Incremental Rehashing: Gradual migration to avoid blocking
- Progressive: Few entries moved per operation
- Dual Tables: Old and new tables coexist during rehashing
Advanced Features:
Expiration Mechanism:
1
2
3
4
5
6
| // Expiration strategies
#define EXPIRE_STRATEGY_ACTIVE 1 // Active expiration
#define EXPIRE_STRATEGY_PASSIVE 2 // Lazy expiration
// Expire dictionary tracks TTL
dict *expires; // Maps keys to expiration times
|
Implementation:
- Active Expiration: Background task removes expired keys
- Passive Expiration: Keys checked on access
- Sampling: Random sampling to find expired keys efficiently
Memory Management:
- Object Encoding: Different encodings for memory efficiency
- Reference Counting: Shared objects for memory savings
- Copy-on-Write: Efficient forking for persistence
Persistence Models:
RDB (Redis Database) Snapshots:
- Fork-based: Child process handles disk I/O
- Compressed: Efficient binary format
- Point-in-time: Consistent snapshots
- Configurable: Various trigger conditions
AOF (Append Only File):
- Command Logging: Every write command logged
- Replayable: Reconstruct state by replaying commands
- Rewriting: Periodic compaction of log files
- Fsync Options: Different durability guarantees
Scaling Patterns:
Replication:
Master ──┬── Slave 1
├── Slave 2
└── Slave N
Features:
- Asynchronous: Non-blocking replication
- Partial Resync: Resume from disconnection point
- Read Scaling: Distribute read load across replicas
Clustering:
Cluster Node 1 ─┬─ Hash Slots 0-5460
Cluster Node 2 ─┼─ Hash Slots 5461-10922
Cluster Node 3 ─┴─ Hash Slots 10923-16383
Implementation:
- Hash Slots: 16384 slots distributed across nodes
- Client-side Routing: Clients calculate target node
- Automatic Failover: Master failures handled automatically
- Resharding: Live migration of slots between nodes
Operation Complexity:
GET/SET: O(1)
LPUSH/RPUSH: O(1)
LRANGE: O(S+N) where S=start, N=elements
SADD: O(1)
SINTER: O(N*M) where N=smallest set
ZADD: O(log(N))
ZRANGE: O(log(N)+M) where M=elements returned
Memory Usage:
- Overhead: ~20-30% overhead for Redis objects
- Encoding Optimization: Automatic selection of efficient encodings
- Compression: Option to trade CPU for memory savings
Production Considerations:
Configuration Tuning:
# Memory
maxmemory 2gb
maxmemory-policy allkeys-lru
# Persistence
save 900 1 # Save if 1 key changed in 900 seconds
save 300 10 # Save if 10 keys changed in 300 seconds
save 60 10000 # Save if 10000 keys changed in 60 seconds
# Network
tcp-backlog 511
timeout 0
tcp-keepalive 300
Monitoring Metrics:
- Memory Usage: Watch for memory pressure
- Command Latency: Monitor P99 latencies
- Connection Count: Track client connections
- Persistence Metrics: RDB/AOF performance
- Replication Lag: Master-slave synchronization delay
Redis’s architecture demonstrates how careful design of data structures, memory
management, and I/O handling can create exceptionally high-performance systems
while maintaining simplicity and reliability.