Redis as a Cache: maxmemory and Eviction Policies
Introduction¶
Used as a cache, Redis needs a memory ceiling and a rule for what to drop when it hits it. Out of the box it has no limit and noeviction — it'll consume all RAM and then start refusing writes.
Set the ceiling¶
# redis.conf
maxmemory 4gb
maxmemory-policy allkeys-lfu
maxmemory should leave room for replication buffers, client buffers, and the
fork COW spike if persistence is on — budget ~25% headroom below physical RAM.
The eight policies¶
| Policy | Evicts | Use when |
|---|---|---|
noeviction |
nothing — writes error with OOM | Redis is a datastore, not a cache |
allkeys-lru |
least-recently-used, any key | general cache, recency matters |
allkeys-lfu |
least-frequently-used, any key | general cache — usually best |
allkeys-random |
random key | uniform access, LRU overhead not worth it |
volatile-lru |
LRU among keys with a TTL | mixed: some keys are permanent |
volatile-lfu |
LFU among keys with a TTL | same, frequency-based |
volatile-random |
random key with a TTL | same, cheap |
volatile-ttl |
key with the shortest remaining TTL | you've encoded priority as TTL |
LFU vs LRU: LRU evicts the key not touched recently, so a burst of one-off
scans can flush your hot set. LFU tracks a frequency counter that decays over
time, so a rarely-but-steadily-used key survives a scan storm. For most caches,
allkeys-lfu is the better default (available since Redis 4).
volatile-* policies fail like noeviction if no keys have a TTL — a common
"why is Redis rejecting writes" surprise. Use allkeys-* unless you genuinely
have a permanent subset.
Tuning LFU¶
lfu-log-factor 10 # higher = counter saturates slower (distinguishes very hot keys)
lfu-decay-time 1 # minutes; how fast the counter decays when idle
Defaults are fine to start. OBJECT FREQ key (requires an LFU policy) shows a
key's counter; OBJECT IDLETIME key (LRU) shows seconds since last access.
How expiry works¶
- Setting
EX/PX/EXPIREstores an expiry timestamp. - Lazy expiry: a key is checked on access; if expired, it's deleted then and a miss is returned.
- Active expiry: a background cycle samples keys with TTLs ~10×/sec and deletes the expired ones, adaptively working harder if it finds many.
- So an expired key can occupy memory until either it's accessed or the active cycle reaches it. Usually negligible; with millions of short-TTL keys it can lag.
Eviction (maxmemory) and expiry (TTL) are independent — a key can be evicted before its TTL, or expire before being evicted.
Verification and troubleshooting¶
redis-cli INFO stats | grep -E 'keyspace_hits|keyspace_misses|evicted_keys|expired_keys'
redis-cli INFO memory | grep -E 'used_memory:|used_memory_rss:|maxmemory:|mem_fragmentation_ratio'
redis-cli --bigkeys
redis-cli MEMORY DOCTOR
Hit rate = keyspace_hits / (keyspace_hits + keyspace_misses).
OOM command not allowed when used memory > 'maxmemory'— policy isnoeviction, or it'svolatile-*and nothing has a TTL. Switch toallkeys-lfu, or add TTLs.- Low hit rate despite plenty of memory — TTLs too short, cache keys include
something high-cardinality (a timestamp, a request id), or the app isn't
actually reading from cache first. Log key patterns with
redis-cli MONITOR(briefly — it's expensive). evicted_keysclimbing — working set exceedsmaxmemory. Raise it, shard, shorten TTLs on bulky low-value keys, or store smaller values (compress, don't cache whole HTML).mem_fragmentation_ratio> 1.5 — allocator fragmentation. Enableactivedefrag yes, or restart. Ratio < 1 means Redis is swapping — bad.- Memory higher than the sum of key sizes — per-key overhead (~50–90 bytes),
or large collections with many small elements.
--bigkeysandMEMORY USAGE key. - Latency spikes on eviction — evicting large keys (a 1 GB hash) blocks.
Break big structures up;
lazyfree-lazy-eviction yesfrees them in a background thread.
Related tools and reading¶
- On-site: Timestamp converter.
- Related posts: Redis persistence: RDB vs AOF, Connection pooling explained.
Stuck on something this site can't fix?Reach out to Prabath directly on LinkedIn.More in Databases & Caching
PostgreSQL Autovacuum Tuning
Autovacuum reclaims dead rows and prevents transaction-ID wraparound. When it falls behind, tables bloat and queries slow — here's the formula and the fixes.
October 24, 2026Database Deadlocks Explained
A deadlock is two transactions each holding a lock the other needs. The engine kills one. Here's how to read the log and remove the cause — usually inconsistent lock order.
October 24, 2026MySQL Replication Basics
How MySQL replication actually works, GTID vs binlog coordinates, and the handful of commands for setup, lag monitoring, and un-breaking a stuck replica.
October 24, 2026