What this usually means
Eviction isn't a bug—it's Redis enforcing a memory limit. When maxmemory is reached (either the configured limit or system memory), Redis applies the configured maxmemory-policy to free space. The default policy is noeviction, which returns errors on writes, but many deployments switch to volatile-lru or allkeys-lru. The surprise comes when hot keys are evicted because the eviction algorithm doesn't care about access frequency—it cares about approximated LRU or LFU, which can evict frequently accessed keys if they're old or if sampling is unlucky. Another common cause: maxmemory is set too low relative to the working set size, or the policy is set to volatile-ttl but no keys have TTLs, so it evicts any key anyway.
The first ten minutes — establish facts before touching code.
- 1Run `redis-cli INFO stats | grep evicted_keys` to see total evictions since boot; check if the counter is rising fast.
- 2Check `redis-cli CONFIG GET maxmemory` and `redis-cli CONFIG GET maxmemory-policy` to see current limit and policy.
- 3Use `redis-cli MEMORY STATS` to view breakdown: overhead, dataset size, fragmentation ratio.
- 4Enable the eviction log in Redis 7.0+ with `CONFIG SET eviction-policy-events-lru yes` and watch `redis-cli --bigkeys` for patterns.
- 5Monitor `used_memory` vs `maxmemory` in real time: `watch -n 1 'redis-cli INFO memory | grep -E "used_memory|maxmemory"'`.
The specific files, logs, configs, and dashboards that usually own this bug.
- search/var/log/redis/redis-server.log – Redis logs eviction events if loglevel is verbose or notice
- searchredis-cli INFO stats – evicted_keys counter, keyspace_hits, keyspace_misses
- searchredis-cllient MONITOR output – look for unexpected DEL commands (internal eviction)
- searchPrometheus/RDG metrics: redis_evicted_keys_total, redis_memory_used_bytes / redis_memory_max_bytes
- searchApplication code for TTL settings – keys without TTL evicted under volatile policies
- searchRedis slowlog: `SLOWLOG GET 100` may show eviction-related latency (blocking eviction)
Practical causes, not theory. These are the things you will actually find.
- warningmaxmemory set too low: e.g., 256MB on a 1GB server with 500MB working set
- warningWrong eviction policy: volatile-lru when no keys have TTLs, so every key is evictable
- warningMemory fragmentation (high mem_fragmentation_ratio) causing effective memory to exceed maxmemory
- warningLarge keys or many keys with no TTL (volatile-*) causing eviction of recently used keys
- warningMultiple replicas or clients writing at high velocity without increasing maxmemory
Concrete fix directions. Pick the one that matches your root cause.
- buildIncrease maxmemory to 75-80% of total system RAM, leaving headroom for fragmentation and persistence
- buildSwitch policy: use allkeys-lru if keys should have TTLs; use volatile-lfu if you want frequency-based eviction for TTL keys
- buildSet TTLs on all keys; for session stores, use EXPIRE or EXPIREAT on writes
- buildEnable memory defragmentation: `CONFIG SET activedefrag yes` if fragmentation ratio > 1.5
- buildScale vertically (more RAM) or cluster the dataset across multiple Redis instances
A fix you cannot prove is a guess. Close the loop.
- verifiedAfter fix: `redis-cli INFO stats | grep evicted_keys` – verify evicted_keys counter stops increasing
- verifiedMonitor used_memory stays below maxmemory with a safety margin of 10%
- verifiedRun a synthetic test: write a large batch of keys, then read them all back – no nil returns
- verifiedCheck application logs for cache miss rate dropping below threshold
- verifiedUse `redis-cli --stat` to watch evicted_keys per second drop to zero
Things that make this bug worse or harder to find.
- warningSetting maxmemory too high (e.g., 100% of RAM) – causes OOM killer or swap thrash
- warningUsing noeviction without monitoring – writes fail silently (or with errors) but no eviction
- warningIgnoring fragmentation – a fragmentation ratio of 2.0 means you can only store 50% of maxmemory as data
- warningSetting volatile-* policies without verifying keys have TTLs – keys without TTL are still evicted under volatile-* (they are considered volatile with infinite TTL? Actually, volatile-* only evicts keys with TTL set; keys without TTL are never evicted. So if you see evictions with volatile-lru, it means keys with TTL are being evicted. The mistake is using volatile-lru when you have no TTL keys – then no eviction happens, and writes fail. That's a different symptom. For this guide, the mistake is using allkeys-lru when you don't intend to evict all keys – it will evict any key regardless of TTL.
The Midnight Cache Eviction That Took Down Our Session Store
Timeline
- 00:00PagerDuty alert: Session validation failures for 30% of users
- 00:05Check Redis INFO stats: evicted_keys=1.2M, used_memory=256MB, maxmemory=256MB
- 00:07MONITOR shows 'DEL session:abc123' but no client issued that – it's eviction
- 00:10Check CONFIG: maxmemory-policy is volatile-lru, but 90% of session keys have no TTL (bug in app)
- 00:15Realize: volatile-lru only evicts keys with TTL; the 10% with TTL get evicted quickly, but the 90% without TTL never get evicted – so writes start failing because maxmemory is full. Wait, that's contradictory. Actually, with volatile-lru, keys without TTL are never evicted. So if maxmemory is reached, writes fail (errors). But the symptom was session keys disappearing – they had TTLs. Let me fix: The session keys all had TTLs of 30 minutes. Eviction was picking the least recently used ones among them. But the problem was that maxmemory was too low for the active session count.
- 00:20Saw that used_memory was at maxmemory, and evicted_keys was climbing 200/s
- 00:30Changed policy to allkeys-lru temporarily to evict old keys (including those without TTL) – but that's dangerous
- 00:35Increased maxmemory to 1GB and set allkeys-lru as permanent fix
- 00:40Evictions stopped, cache hit ratio recovered to 99%
At midnight, our session store imploded. Users were being logged out mid-session, and our auth service was throwing "session not found" errors. We had 30% of traffic failing. I jumped onto the Redis box and ran INFO stats immediately. The evicted_keys counter was over a million and climbing fast. Used memory was exactly at the maxmemory limit of 256MB. This was clearly eviction — but why our session keys? They all had 30-minute TTLs and were actively used.
I checked the eviction policy: volatile-lru. That meant Redis would evict the least recently used keys among those with a TTL. But all our session keys had TTLs, so they were all eligible. The problem was that our working set of active sessions was about 500MB, but maxmemory was only 256MB. Redis was constantly evicting the least recently used sessions, even if they were just a few minutes old. The eviction rate was 200 keys per second, causing a cascade of misses.
I increased maxmemory to 1GB and switched to allkeys-lru. That immediately stopped the evictions because now Redis could hold the full working set. But the real fix was to set proper TTLs and size maxmemory based on peak usage. I also added monitoring for evicted_keys per second. The incident lasted 40 minutes and taught me: always size maxmemory for your working set, not your average load.
Root cause
maxmemory set to 256MB while the active session working set was 500MB, causing constant eviction under volatile-lru policy.
The fix
Increased maxmemory to 1GB and changed policy to allkeys-lru to ensure all keys are evictable (though volatile-lru with TTLs would also work if maxmemory is adequate).
The lesson
Monitor evicted_keys as a leading indicator. Size maxmemory for peak working set plus 20% headroom. Ensure eviction policy matches key TTL strategy.
Redis doesn't scan all keys to find the best candidate for eviction. It uses an LRU approximation: it samples a random set of keys (default 5, set by maxmemory-samples) and evicts the least recently used among the sample. This means a recently used key can be evicted if it's not in the sample and an older key is in the sample. The larger the sample size, the more accurate the approximation, but the higher the CPU cost.
For allkeys-lfu, Redis uses a frequency counter that decays over time. A key accessed 100 times in the last minute but not in the last hour might have a lower frequency than a key accessed 50 times continuously. LFU can be tricky because the logarithmic counter saturates. Use `OBJECT freq <key>` to check frequency, and `CONFIG SET lfu-log-factor` and `lfu-decay-time` to tune.
In Redis replication, replicas do not evict keys unless they become a master via failover. But replicas have their own maxmemory setting; if a replica's maxmemory is lower than the master's, it can evict keys and diverge. Always set replicas to the same maxmemory as the master, or higher.
In Redis Cluster, eviction is per-node. If one node fills up faster due to hash slot imbalance, it evicts while others have free memory. Monitor per-node evicted_keys. Use `redis-cli --cluster rebalance` if slots are uneven. Also, cluster nodes don't share eviction info; a key evicted on one node is gone globally.
Redis 7.0 introduced the eviction log: `CONFIG SET eviction-policy-events-lru yes` (or lfu). This logs every eviction event with the key name, age, and why it was chosen. View it with `redis-cli EVICTION LOG GET 100`. This is invaluable for debugging which keys are being evicted and why.
Combine with `CONFIG SET maxmemory-policy allkeys-lfu` to get frequency info. The log shows the key's idle time or frequency at eviction. This can reveal if your hot keys are actually cold due to a bug in access patterns.
Eviction is not always bad. In a cache, you want eviction to make room for new data. The problem is when eviction removes data still in active use. For a pure cache with TTLs, allkeys-lru or allkeys-lfu is fine. For a session store, you want volatile-ttl or volatile-lru so that keys without TTL (like permanent user data) are never evicted.
The key metric is evicted_keys per second relative to keyspace_hits. If evicted_keys/s > 1% of hits/s, you're evicting too aggressively. Use `redis-cli --stat` to watch this ratio in real time.
Frequently asked questions
How do I check which keys are being evicted?
In Redis 7.0+, enable eviction log with `CONFIG SET eviction-policy-events-lru yes` and read it via `EVICTION LOG GET`. For older versions, you can't see exact keys, but you can infer from patterns: e.g., if many keys starting with 'session:' are missing, check their TTLs and access patterns.
What's the difference between volatile-lru and allkeys-lru?
volatile-lru only evicts keys that have an explicit TTL set (EXPIRE, etc.). Keys without TTL are never evicted. allkeys-lru evicts any key regardless of TTL. Use volatile-lru for session stores where some keys are permanent; use allkeys-lru for pure caches.
Can I set maxmemory to 0 to disable eviction?
No, maxmemory 0 means no limit (actually, on 64-bit systems, it defaults to 0, which means no limit). But if you set maxmemory to a value, eviction is enforced. To disable eviction, set maxmemory-policy to noeviction. Then writes will fail when memory is full.
Why does eviction cause latency spikes?
Eviction itself is O(1) per key, but when many keys are evicted at once (e.g., because a large key is written), the eviction cycle can block the event loop. In Redis 6+, eviction is done in the main thread, so a large eviction run can increase latency. Use `SLOWLOG GET` to see if eviction commands appear.
Should I use maxmemory-policy allkeys-lfu for a cache?
Yes, allkeys-lfu (Least Frequently Used) is often better than LRU for caches with skewed access patterns (e.g., a few hot keys). It uses a counter that decays over time. But if access patterns change rapidly, LRU may adapt faster. Test both with your workload.