Key design, memory and eviction
Naming conventions that scale, per-key memory accounting, finding big keys, and choosing a maxmemory policy that will not surprise you.
Key design
# colon-separated, type-prefixed keys
SET user:42:profile:1 '{"name":"Ada"}'
HSET cart:42:items sku1 2 sku2 1
ZADD board:daily:2026-09-18 1500 user:42
EXPIRE user:42:profile:1 3600
# hash tags force related keys onto the same cluster slot
# user:{42}:profile user:{42}:orders -> both hash to slot(user:{42})
# note the braces: only the part inside them is used for the slot- Prefix with the entity type, then the identifier, then the attribute. A key you cannot understand from
MONITORoutput is a key you will not be able to debug. - Never build a key from unbounded user input without a size limit - a 1 MB key is legal and will be replicated to every replica.
- Hash tags are required for multi-key operations in cluster mode, but overusing one tag defeats sharding by putting everything on a single slot.
- Store a version or a schema marker where the shape can change, so a deploy can distinguish old and new formats instead of crashing on a decode error.
⚠️
Without
maxmemory and a policy, Redis grows until the operating system kills it - typically by the OOM killer, with no useful log. Setting a limit turns exhaustion into a controlled eviction or a clean write error.Memory accounting
127.0.0.1:6379> MEMORY USAGE user:42:profile # bytes for one key
127.0.0.1:6379> MEMORY DOCTOR
127.0.0.1:6379> MEMORY STATS | head -30
127.0.0.1:6379> info memory
# big keys: sampled, but far cheaper than a full walk
redis-cli --bigkeys
redis-cli --memkeys --memkeys-samples 0
# keys with no expiry are the usual cause of unbounded growth
redis-cli --scan --pattern 'cache:*' | head -1000
# why the used_memory number is bigger than your data
# - per-key overhead: a small key is not small
# - allocator fragmentation: mem_fragmentation_ratio
# - client output buffers, replication backlog, AOF buffer| Setting | Meaning | Practical value |
|---|---|---|
maxmemory | Hard limit before the policy applies | 70-80 percent of container memory, leaving room for COW during fork |
maxmemory-policy | What to evict when full | allkeys-lru for a pure cache |
maxmemory-samples | Keys sampled to approximate LRU | 5 by default; 10 improves accuracy slightly |
maxmemory-clients | Cap on client buffers | Protects against a slow subscriber |
activedefrag | Background defragmentation | jemalloc only; watch the CPU cost |
- A key holding one 8-byte value still costs roughly 60-100 bytes of overhead. Ten million tiny keys cost a gigabyte before any data.
- Small hashes and lists use compact encodings (listpack) and are far cheaper per element than a key per item.
- Empty keys disappear: deleting the last field of a hash removes the key, which is convenient and can surprise code that expects
EXISTSto keep returning 1.
Eviction policies
CONFIG SET maxmemory 4gb
CONFIG SET maxmemory-policy allkeys-lru
CONFIG SET maxmemory-samples 10
# check what is actually happening
INFO stats | grep evicted_keys
INFO memory | grep -E "maxmemory_policy|mem_fragmentation_ratio"| Policy | Evicts | Risk |
|---|---|---|
noeviction | Nothing, writes fail with OOM | Correct for a queue or a lock store - you lose writes, not data |
allkeys-lru | Least recently used of all keys | Evicts a session or lock along with cache entries |
allkeys-lfu | Least frequently used | Better when a small hot set matters; needs time to learn |
volatile-lru | LRU among keys with a TTL | Nothing is evicted if no keys have a TTL, so writes still fail |
volatile-ttl | Keys closest to expiry | Predictable, but ignores access frequency |
# never mix cache and durable data in one instance
# instance A: cache only
maxmemory-policy allkeys-lru
# instance B: locks, queues, sessions
maxmemory-policy noeviction
# and alert on evicted_keys > 0 and on rejected_connectionsMixing a cache and a queue in one instance means the cache policy can silently delete queued work. Separate instances, or at minimum separate volatile-* semantics with every cache key carrying a TTL and every durable key carrying none.
FAQ
How do I find what is using all the memory?
Start with
INFO memory for the totals and MEMORY STATS for the breakdown, then redis-cli --bigkeys for outliers. In practice the answer is usually one key pattern with no TTL, not a thousand different keys.What fragmentation ratio is a problem?
A ratio above about 1.5 means the allocator holds memory it is not using. Restarting usually resolves it; enabling active defragmentation trades CPU for memory. A ratio below 1 means swapping, which is far worse - check the host immediately.
Related
Installing Redis, redis-cli and RESP basics Security: ACLs, TLS and hardening
Last refreshed 2026-09-18.