Skip to main content

Redis: Overview & Architecture

Redis (Remote Dictionary Server) is an open-source, in-memory data structure store used as a database, cache, message broker, and streaming engine. Its combination of simplicity, speed, and rich data structures makes it ubiquitous in production systems.


Why Redis is Fast

Redis is often described as "single-threaded" — but this requires nuance:

Single-threaded event loop (command processing)

Epoll/Kqueue (I/O multiplexing — handles thousands of connections)

Background threads (AOF fsync, object eviction, lazy delete)

Single-Threaded Command Execution

All Redis commands execute sequentially in a single thread. This design:

  • Eliminates locking overhead (no mutexes needed for data structures)
  • Makes all operations atomic by default
  • Simplifies reasoning about state consistency
  • Avoids context-switching overhead between threads

Redis 6.0+: Added I/O threading — network reads/writes are parallelized while command execution remains single-threaded. This removes the I/O bottleneck for high-connection workloads.

I/O Multiplexing with Epoll

Redis uses epoll (Linux) / kqueue (macOS) / IOCP (Windows) to handle thousands of simultaneous client connections with a single thread:

Thousands of client connections

epoll (single syscall monitors all file descriptors)

Event loop: "these 47 clients have data ready"

Process each client request sequentially

This is the same model used by Node.js and Nginx — efficient for I/O-bound workloads where commands are fast.


Memory Architecture

Redis stores all data in RAM (optionally persisted to disk):

Memory Layout:
┌─────────────────────────────────────┐
│ Redis Object (robj) │
│ ├── type (string, list, hash...) │
│ ├── encoding (ziplist, hashtable) │
│ └── ptr → actual data │
└─────────────────────────────────────┘

Memory Encoding Optimization

Redis automatically uses compact encodings for small collections to save memory:

Data TypeSmall EncodingLarge EncodingThreshold
Hashziplist/listpackhashtable>128 fields or field >64 bytes
Listlistpack/quicklistquicklist>128 elements or element >64 bytes
Setlistpack/intsethashtable>128 elements
Sorted Setlistpackskiplist + hashtable>128 elements
Stringint (raw int)embstr/raw>20 chars

Why this matters: A Redis hash with <64 fields uses a flat array (ziplist) instead of a full hash table — dramatically reducing memory overhead. Designing your key structure to stay within these thresholds is a key performance optimization.


Redis Data Persistence

ModeMechanismRecovery PointUse Case
RDB (Snapshot)Fork + binary dump at intervalsAt last snapshotFast recovery, small files
AOF (Append-Only File)Log every write commandNear real-timeDurability, audit trail
No persistencePure in-memoryData lost on restartCache-only deployments
RDB + AOFBoth modes combinedAOF granularityProduction recommended
# RDB: save snapshot every 60s if ≥1000 changes
save 60 1000

# AOF: sync every second (compromise between durability and performance)
appendfsync everysec
# Options: always (safest), everysec (default), no (fastest, risky)

RDB fork() latency: When Redis forks to create a snapshot, the kernel must copy page tables. On a 10 GB instance, this fork can cause a 50–100ms latency spike. Use latency monitor to detect this.


Redis Architecture Patterns

Standalone

Single Redis instance — simple but single point of failure.

Sentinel (High Availability)

Master ──→ Replica 1
──→ Replica 2
Sentinel 1 / Sentinel 2 / Sentinel 3 (quorum-based monitoring)
  • Sentinels vote to promote a replica if master fails
  • Client libraries use Sentinel to discover the current master
  • Failover time: typically 10–30 seconds

Cluster (Horizontal Scaling)

   Slot 0–5460        Slot 5461–10922     Slot 10923–16383
[Master A] [Master B] [Master C]
[Replica A] [Replica B] [Replica C]
  • Hash slots (0–16383) sharded across masters
  • CLUSTER KEYSLOT mykey → tells you which slot a key maps to
  • Keys in different slots cannot be used in multi-key operations
  • Use hash tags {user}.orders and {user}.profile to force co-location on same slot

Common Use Cases

Use CaseRedis Features UsedSenior Consideration
Session storeStrings + TTLSticky sessions vs distributed session
Rate limitingINCR + EXPIRE or Sorted SetsToken bucket vs sliding window
Distributed locksSET NX EX (Redlock)Clock drift, lock renewal, fencing tokens
LeaderboardsSorted SetsZRANGEBYSCORE vs ZRANGEBYRANK
Real-time feedsStreams or Pub/SubAt-most-once vs at-least-once delivery
CacheStrings/Hashes + TTL + evictionStampede, dogpile, warm-up strategy
QueueLists (BLPOP/RPUSH) or StreamsVisibility timeout, dead-letter queue
GeospatialGEO commandsGEORADIUS for proximity queries

Redis vs Memcached

RedisMemcached
Data typesRich (12+ types)Strings only
PersistenceRDB + AOFNone
ReplicationBuilt-inNone (requires client)
Pub/SubYesNo
Lua scriptingYesNo
ClusterNative clusterConsistent hash (client-side)
ThreadingSingle-threaded exec + I/O threadsMulti-threaded
Memory efficiencySlightly higher overheadLower overhead for simple strings

Choose Redis for almost all new projects. Choose Memcached only for pure string caching at extreme throughput where Redis Cluster latency is measurable.


Redis Command Complexity Reference

CommandComplexityNotes
GET, SET, INCRO(1)Hash table lookup
HGETALLO(n)n = number of fields
LRANGEO(S+N)S = offset from head, N = elements returned
ZADDO(log N)Skip list insertion
SMEMBERSO(n)Returns all members
SORTO(N+M*log(M))N = elements, M = returned elements — dangerous on large sets
KEYS patternO(n)Never use in production — use SCAN instead

Production rule: Never run KEYS * in production — it blocks the single-threaded event loop and causes latency spikes. Use SCAN with a cursor instead.


Key Naming Conventions

# Pattern: object-type:id:field
user:1234:profile
user:1234:sessions

# Hash tag for cluster slot co-location
{user:1234}:profile
{user:1234}:orders # same slot as above
{user:1234}:sessions

# Versioned keys for safe migrations
user:v2:1234:profile

# Avoid: key names longer than 100 bytes waste memory
# Avoid: key names with spaces or special chars (use : and _ as delimiters)