Redis Memory Management and Eviction
The maxmemory limit, eviction policies, how approximated LRU/LFU behave, and the memory overhead behind Redis memory operations
When Redis reaches its memory limit it either picks keys to discard or refuses writes. The eviction policy decides which.
The moment memory hits the ceiling
Redis keeps its working dataset in memory and can persist it to disk. Configure both the maxmemory limit and the eviction policy that selects keys when memory exceeds it.
maxmemory sets the memory limit used to decide eviction. Redis counts data and management overhead, but excludes some replication and AOF (append only file) buffers. Total process memory can therefore exceed this limit.
maxmemory 8gb
maxmemory-policy allkeys-lru
maxmemory-samples 10These values are an example for an 8GiB cache. maxmemory-policy selects the eviction rule, and maxmemory-samples sets the candidate sample size used by approximate policies.
How eviction proceeds
Redis checks memory during command processing and evicts keys according to policy when the limit is exceeded. If no candidate exists or eviction is disabled, commands that allocate memory can fail.
The last arrow returns to the check. Redis removes one key at a time and repeats until memory drops below the limit. Running right up against the limit therefore means eviction interleaves with every write and latency can rise.
The eight eviction policies
Policies split along two axes: whether the candidates are all keys (allkeys-) or only keys with an expiry (volatile-), and what criterion decides the discard. Expiry here refers to the TTL (time to live) set per key.
| Policy | Candidates | Recommended for | Behavior |
|---|---|---|---|
allkeys-lru | All keys | General caching | Discards least recently used keys first |
allkeys-lfu | All keys | Access concentrated on a few hot keys | Discards least frequently accessed keys first |
volatile-lru | Keys with a TTL | Mixed short-lived and long-lived data | Protects keys without a TTL |
volatile-lfu | Keys with a TTL | Hot data that carries a TTL | Frequency-based eviction |
volatile-ttl | Keys with a TTL | Explicit expiry times | Nearest expiry goes first |
volatile-random | Keys with a TTL | Rarely used | Random selection |
allkeys-random | All keys | Rarely used | Random selection |
noeviction | No eviction | Queues and job stores | Rejects writes past the limit |
Redis defaults to noeviction. A general cache can consider allkeys-lru, while volatile-* evicts only keys with a TTL. The eviction documentation explains that writes can fail when eligible keys are insufficient.
How approximated LRU and LFU work
LRU (least recently used) discards the key unused for the longest time. Sorting every key to find the true minimum is expensive, so Redis uses an approximated LRU rather than an exact one.
Approximated LRU samples a handful of keys instead of scanning all of them and discards the oldest among the sample. maxmemory-samples sets that sample size.
maxmemory-samples 10Larger samples improve the chance of finding an older key but increase CPU cost. Compare the default of 5 with the example value of 10 on real access patterns, measuring miss ratio and latency.
LFU (least frequently used) uses a different criterion. It discards the least frequently accessed key rather than the least recently used one. Supported since Redis 4.0, it beats LRU under a Pareto pattern where access concentrates on a subset of keys.
key1: accessed 1000 times
key2: accessed 5 times
key3: accessed 8 times
LRU -> evicts by least recent use (access count is irrelevant)
LFU -> key2 and key3 are more likely to go first (depends on decay and sampling)LFU tracks frequency with an 8-bit probabilistic logarithmic counter per key. Its decay reduces old popularity over time, so formerly hot keys are not protected indefinitely. Low-frequency keys are more likely to be evicted, but the order is not guaranteed.
Memory usage exceeds the data
Actual memory usage does not equal the size of the stored data. Allocator fragmentation and various buffers add to it. The window into that difference is INFO memory.
redis-cli INFO memoryThe following is selected output from a small Redis 8.10.1 instance using libc on macOS, measured in 2026-09.
used_memory:1553968
used_memory_rss:4554752
mem_fragmentation_ratio:2.97
mem_allocator:libc
mem_not_counted_for_evict:0
mem_replication_backlog:0
mem_aof_buffer:0mem_fragmentation_ratio divides RSS by memory allocated by Redis. Small datasets can show high ratios soon after startup, as in this measurement; a threshold such as 1.5 alone does not prove severe fragmentation. Check absolute RSS overhead and allocator metrics together.
Active defrag relocates memory while Redis runs to reduce fragmentation. It requires a supported build with the Redis-specific jemalloc allocator; the following enabling configuration does not apply to libc builds.
activedefrag yes
# ignore fragmentation under 10MB
active-defrag-ignore-bytes 10mb
# run at 10% fragmentation or above
active-defrag-threshold-lower 10mem_not_counted_for_evict deserves attention alongside it. It represents memory excluded from eviction, such as replication and AOF buffers. When this value is large, the process as a whole uses more memory even while used_memory stays under the limit.
Reducing memory usage
Compact encodings can reduce memory for small collections. Hashes and Lists use listpack depending on version and size; larger Lists use quicklist to link multiple listpack nodes.
# stays a listpack at 512 fields or fewer
hash-max-listpack-entries 512
# stays a listpack when values are 64 bytes or less
hash-max-listpack-value 64
# 8KiB per List node
list-max-listpack-size -2A Hash becomes a hashtable when its entry count or field-name/value lengths exceed the thresholds. For Lists, -2 means 8KiB per node; a positive value means entries, not bytes. Compare MEMORY USAGE on actual data to evaluate grouping related fields into a small Hash.
redis_evicted_keys_total is cumulative, so use its increase or rate over a time window to detect current eviction. Treat 80% memory use or a fragmentation ratio of 1.5 as estimated initial alert thresholds, then adjust to the baseline and available memory.
Choosing a policy
A general cache can evaluate allkeys-lru first. If the workload has persistently popular keys, compare its miss ratio with allkeys-lfu. Choose based on actual access patterns and whether data eviction is acceptable.
Pick volatile-* only when you want to discard keys with a TTL exclusively. That applies when cache and durable data share one instance and keys without a TTL must never be lost. Watch the ratio of keys carrying a TTL, though: too few candidates and writes can be blocked because there is nothing to discard.
noeviction suits queues or job stores where data must not be dropped. Past the limit it rejects writes instead of deleting keys, so the application has to be ready to handle that error.
Summary
maxmemory limits memory counted for eviction, while the policy decides which keys Redis may remove. Compare LRU and LFU against actual cache access patterns, and handle allocation errors when eviction cannot free space.
Process memory also includes overhead and buffers, so monitor RSS and allocator metrics alongside the dataset. Confirm compact encodings and allocator support before changing listpack or active defrag settings.