Redis Memory Usage Keeps Growing? Troubleshooting Eviction Policies, Fragmentation, and Persistence Overhead
Create Time:2026-09-15 15:45:04
浏览量
1069

redis-memory-usage-growth-en-cover.pngRedis memory growth does not always mean that the dataset is leaking. The database may genuinely contain more keys, expired data may not be disappearing as expected, the allocator may be holding fragmented pages, or background persistence and replication may be creating temporary overhead. Treating all of these cases as “a big key problem” often leads to the wrong fix.

This guide provides a production-oriented investigation order for Redis on Linux. It explains how to distinguish dataset growth from RSS growth, how to review TTL and eviction behavior, and how to account for fragmentation, clients, replication, RDB snapshots, and AOF rewrites before changing configuration.

Documentation checked on September 15, 2026. Command output and available metrics can vary by Redis version and managed-service restrictions. Verify the documentation for your deployed version before changing a production instance.

1. Establish which memory number is growing

Start with Redis itself instead of relying only on top, free, or a container dashboard:

redis-cli INFO memory
redis-cli MEMORY STATS

The first comparison is between these metrics:

  • used_memory: memory allocated by Redis through its allocator.

  • used_memory_dataset: the portion associated with the dataset after internal overhead is removed.

  • used_memory_rss: physical memory attributed to the Redis process by the operating system.

  • mem_fragmentation_ratio: the relationship between RSS and allocated memory.

  • maxmemory: the configured memory limit; 0 normally means no explicit Redis limit on a 64-bit system.

  • mem_not_counted_for_evict: memory excluded from eviction calculations, including certain replication and AOF buffers.

Interpret the trend, not a single ratio. If used_memory_dataset, key count, and write volume rise together, the dataset is probably growing. If used_memory stabilizes but RSS remains much higher, fragmentation or allocator retention is more likely. If memory jumps during BGSAVE or BGREWRITEAOF, copy-on-write overhead needs attention.

Record a baseline before making changes:

date
redis-cli INFO memory
redis-cli INFO keyspace
redis-cli INFO persistence
redis-cli INFO replication
redis-cli INFO clients

Taking the same snapshot every few minutes during the incident is more useful than comparing unrelated dashboard peaks.

2. Check whether the keyspace is actually expanding

Count keys and inspect database-level TTL statistics:

redis-cli DBSIZE
redis-cli INFO keyspace

For each database, INFO keyspace reports keys, expires, and an average TTL sample. A rapidly increasing key count with a small expires value is a strong sign that cache entries are being written without a lifetime. A stable key count with rising memory suggests that existing values may be getting larger.

Use Redis CLI sampling tools during a low-traffic period:

redis-cli --bigkeys -i 0.1
redis-cli --memkeys -i 0.1

These scans traverse the keyspace. The -i delay reduces pressure but does not make a full scan free. On a busy or very large production database, test the command on a replica or run it with strict operational monitoring.

For a suspected key, inspect its size and encoding:

redis-cli MEMORY USAGE app:session:123 SAMPLES 5
redis-cli TYPE app:session:123
redis-cli OBJECT ENCODING app:session:123
redis-cli PTTL app:session:123

Avoid KEYS * in production. Use cursor-based SCAN when you need application-specific sampling, and do not delete a large collection synchronously without understanding the latency impact. UNLINK can move much of the reclamation work off the main thread, but the command still changes live data and should follow an approved cleanup plan.

3. Review TTL behavior before changing eviction

Expiration and eviction solve different problems. Expiration removes a key after its TTL. Eviction selects keys when Redis reaches maxmemory and a command needs more memory.

Check representative keys from each namespace:

redis-cli PTTL cache:product:1001
redis-cli PTTL session:9f82
redis-cli PTTL rate-limit:203.0.113.8

Typical warning signs include:

  • cache-writing code paths that omit EX, PX, EXAT, or PXAT;

  • refresh jobs that replace values and accidentally remove or extend the intended TTL;

  • mixed persistent and expiring keys under a volatile-* eviction policy;

  • a burst of keys with nearly identical expiration times, creating an avoidable cleanup spike.

Fix TTL ownership in the application when possible. Retrofitting TTLs with a one-off script can be useful, but it is a data mutation and should be rate-limited, audited, and tested on a representative sample first.

4. Confirm the memory limit and eviction policy

Read the active configuration:

redis-cli CONFIG GET maxmemory
redis-cli CONFIG GET maxmemory-policy
redis-cli CONFIG GET maxmemory-samples

Common policies behave differently:

  • noeviction rejects memory-growing writes after the limit is reached. It is appropriate when Redis must not discard data automatically.

  • allkeys-lru evicts approximately least-recently-used keys from the entire keyspace and is common for general caches.

  • allkeys-lfu favors frequently accessed keys when request frequency is more useful than recent access.

  • volatile-lru, volatile-lfu, volatile-random, and volatile-ttl select only keys with an expiration. If few keys have TTLs, they may provide little room to recover.

  • allkeys-random has low selection overhead but does not preserve hot data deliberately.

Do not copy a policy from another environment without classifying the workload. An all-keys policy can delete persistent application state. A volatile-only policy can behave like noeviction when no eligible expiring keys remain.

Also check whether eviction is already occurring:

redis-cli INFO stats | grep -E 'evicted_keys|expired_keys|keyspace_hits|keyspace_misses'

Rising evicted_keys confirms pressure, but it is not automatically healthy. Correlate it with latency, miss rate, backend load, and application errors. A cache that avoids out-of-memory errors by continuously evicting useful data can still overload its origin database.

5. Diagnose fragmentation and allocator retention

A high mem_fragmentation_ratio is a clue, not a complete diagnosis. MEMORY STATS provides more detail, including allocator fragmentation, allocator RSS, and RSS overhead. Linux may keep pages assigned to the process even after Redis frees objects, so RSS does not always fall immediately with used_memory.

Run the built-in diagnosis and review active defragmentation settings:

redis-cli MEMORY DOCTOR
redis-cli CONFIG GET activedefrag

If the Redis build and service allow it, active defragmentation can gradually reorganize allocations. It consumes CPU, so enable or tune it only after measuring latency and fragmentation. Do not restart a primary merely to make RSS look smaller unless failover, persistence, and recovery time have been tested.

MEMORY PURGE asks the allocator to release reclaimable pages. It is best-effort, may have no visible effect, and should not be treated as a substitute for fixing the allocation pattern:

redis-cli MEMORY PURGE

Before using it in production, confirm allocator support and run it during a controlled window.

6. Account for RDB and AOF copy-on-write overhead

RDB snapshots and AOF rewrites normally fork a background process. Linux initially shares memory pages between parent and child. Pages modified while the child is working are copied, so a write-heavy workload can produce a large temporary memory peak even when the dataset is stable.

Inspect persistence and fork-related metrics:

redis-cli INFO persistence
redis-cli INFO stats | grep latest_fork_usec

Look for a memory rise that aligns with rdb_bgsave_in_progress or aof_rewrite_in_progress, and review the available copy-on-write metrics for your Redis version. A long-running snapshot, slow storage, or a high write rate increases the period in which pages can be copied.

Practical mitigations include reducing unnecessary write amplification, scheduling manual maintenance away from traffic peaks, improving storage throughput, and leaving enough host or container headroom for a fork. Disabling persistence changes the durability model and should never be used as a quick memory fix without a recovery design.

7. Inspect clients, replication, and buffers

Dataset memory is only part of the process footprint. Slow clients, Pub/Sub consumers, replica lag, replication backlog, and AOF buffers can all grow.

Check the relevant sections:

redis-cli INFO clients
redis-cli INFO replication
redis-cli CLIENT LIST
redis-cli MEMORY STATS

Pay attention to blocked clients, unusually large output buffers, replica disconnections, and a replica that cannot consume data as quickly as the primary produces it. Review client-output-buffer-limit before changing it. A limit that is too high permits more memory growth; one that is too low may disconnect legitimate slow consumers.

Remember that some buffer memory is excluded from maxmemory eviction accounting. This is why a process can approach the Linux or container limit even though used_memory appears to remain near the configured Redis ceiling.

8. Choose the fix based on the failure mode

Use evidence from the previous steps:

  • If keys and dataset memory grow: fix retention, TTLs, cardinality, or oversized values; then plan safe cleanup or capacity expansion.

  • If eviction cannot find eligible keys: choose a policy aligned with whether Redis is a cache or a durable data store, and correct TTL coverage.

  • If RSS stays high while dataset memory falls: investigate allocator fragmentation, deletion patterns, active defragmentation, and controlled maintenance.

  • If peaks coincide with persistence: reserve copy-on-write headroom and reduce the duration or write intensity of background operations.

  • If buffers grow: identify slow clients, replication lag, Pub/Sub backpressure, or AOF pressure before raising limits.

  • If the container is being killed: compare Redis metrics with cgroup limits and host memory; Redis cannot manage headroom it cannot see or is not configured to reserve.

Do not set maxmemory equal to the machine or container limit. Leave capacity for the operating system, allocator overhead, client and replication buffers, persistence copy-on-write, monitoring agents, and workload bursts. The correct margin depends on write rate, dataset shape, persistence mode, and failover design.

A compact production checklist

redis-cli INFO memory
redis-cli MEMORY STATS
redis-cli INFO keyspace
redis-cli INFO persistence
redis-cli INFO replication
redis-cli INFO clients
redis-cli INFO stats
redis-cli CONFIG GET maxmemory
redis-cli CONFIG GET maxmemory-policy
redis-cli MEMORY DOCTOR

Capture the outputs with timestamps, compare them with application deploys and traffic changes, and change one variable at a time. That makes it possible to distinguish a real leak from expected dataset growth or temporary operating overhead.

Conclusion

When Redis memory keeps growing, first separate dataset growth from process RSS. Then verify key count, value size, TTL coverage, maxmemory, and the eviction policy. If the dataset is stable, move on to fragmentation, persistence forks, client buffers, and replication. This sequence prevents risky configuration changes and makes the final decision—cleanup, policy adjustment, resharding, or capacity expansion—based on measurable evidence.

References

  1. Redis documentation: INFO command — checked September 15, 2026

  2. Redis documentation: MEMORY STATS — checked September 15, 2026

  3. Redis documentation: Key eviction — checked September 15, 2026

  4. Redis documentation: Memory optimization — checked September 15, 2026

  5. Redis documentation: Redis persistence — checked September 15, 2026

  6. Redis documentation: MEMORY DOCTOR — checked September 15, 2026