The robj structure, SDS strings, jemalloc size classes, memory fragmentation ratio, active defragmentation, MEMORY USAGE per key, and practical strategies for cutting Redis memory usage by 50%.
F-10 — Memory Layout and Object Encoding Internals
Who this module is for: You know Redis is fast because it uses RAM, but you have never understood how it uses RAM — what a key actually costs, why two seemingly similar datasets can have wildly different memory footprints, or why your 100MB dataset grows to 300MB in Redis. This module covers the memory model,
jemalloc, memory fragmentation, the object encoding hierarchy, and practical tools for diagnosing and reducing Redis memory usage.
How Redis Stores Data in Memory
When you run SET user:1001 "Jatin", Redis does not simply store the bytes "Jatin" in memory. It creates a chain of C structures:
- The hash table entry — a pointer in the global keyspace hash table pointing to the key object
- The key object (robj) — a Redis Object structure containing type, encoding, reference count, LRU clock, and a pointer to the actual data
- The key string (SDS) — a Simple Dynamic String: 4 bytes of header (length, free space) + the key bytes + null terminator
- The value object (robj) — same structure as the key object
- The value data — the actual value bytes (or a pointer to a more complex structure like a listpack or skiplist)
For a key user:1001 (9 bytes) with value "Jatin" (5 bytes), the actual memory consumption is roughly:
| Structure | Approximate Size |
|---|---|
| Hash table entry (two pointers) | 16 bytes |
| Key robj | 16 bytes |
| Key SDS header | 4 bytes |
| Key bytes ("user:1001\0") | 10 bytes |
| Value robj | 16 bytes |
| Value SDS / int encoding | 8–16 bytes |
| jemalloc overhead (rounding to bin size) | 0–24 bytes |
Total: roughly 80–100 bytes for a 14-byte key-value pair. The ratio of overhead to payload is high for small values. This is why key naming matters — short keys reduce overhead, and large values (like JSON blobs) amortize the per-key cost better.
Simple Dynamic Strings (SDS)
Redis stores all strings (keys and String-type values) as SDS — its own string implementation, not C's null-terminated strings.
SDS header (as of Redis 3.2+, the header size depends on string length):
SDS allows O(1) length lookup (no scanning for null terminator), binary safety (can store null bytes in the middle), and pre-allocated capacity for append operations (reducing reallocation frequency).
The key insight: every SDS has at least 1 byte of overhead beyond the data itself, and jemalloc rounds allocations to power-of-2 bin sizes. A 9-byte key ("user:101") gets a 16-byte allocation (next power of 2 after 9 + 1 header byte = 10 bytes).
The Redis Object (robj)
Every Redis value is wrapped in an robj (Redis Object):
This structure is 16 bytes on a 64-bit system.
Shared integers: Redis pre-allocates objects for integers 0–9999. SET counter 42 stores the integer 42 as a pointer to a pre-allocated shared object — no allocation needed. This is why OBJECT REFCOUNT counter returns a number > 1 for small integers (multiple keys may point to the same shared object).
jemalloc and Memory Fragmentation
Redis uses jemalloc as its memory allocator. jemalloc manages memory in size classes (bins): 8, 16, 32, 64, 128, 192, 256 bytes, etc. When Redis requests 10 bytes, jemalloc gives it a 16-byte slot. The 6 unused bytes are "internal fragmentation."
Memory fragmentation ratio:
mem_fragmentation_ratio = used_memory_rss / used_memory
used_memory— what Redis believes it is using (its allocations)used_memory_rss— what the OS reports Redis is using (RSS = Resident Set Size)
| Ratio | Interpretation |
|---|---|
| ~1.0 | Healthy — very little fragmentation |
| 1.1–1.5 | Acceptable — some fragmentation, normal for dynamic workloads |
| > 1.5 | High fragmentation — wasted memory, consider defragmentation |
| < 1.0 | Redis has swapped some data to disk (very bad — indicates memory pressure) |
Causes of Fragmentation
- Key churn — many keys created and deleted over time leaves holes in the allocator's free lists
- Resizing data structures — Hash or List encoding upgrades (listpack → hashtable) free the old structure and allocate a new one; the old slot may not be immediately reusable
- Long-running instances — fragmentation accumulates over time
Active Defragmentation
Sign in to keep reading
The rest of this module is free — sign in with Google to unlock it and track your progress.
Sign in & RegisterDiscussion
0Join the discussion