Databases

Redis Slowdown Playbook: Big Keys, Blocking Commands and Eviction

Redis processes commands on a single thread, so one blocking hotspot slows the whole instance. This guide follows the order "memory level → big keys → blocking commands → eviction and persistence", each step with copy-ready commands.

By 巧匠 Team·8 min read·Updated 2026-08-30

Memory first: full or fragmented

`INFO memory` is the first stop: `used_memory_human` is actual usage and `maxmemory` the cap (0 means unlimited — dangerous, the OS OOM-killer will step in); a `mem_fragmentation_ratio` above 1.5 indicates fragmentation, common with frequent churn of large values.

When memory is full and the policy is noeviction, writes fail with OOM errors; the application symptom is "intermittent write failures", easily misdiagnosed as a network issue.

redis-cli INFO memory | grep -E "used_memory_human|maxmemory_human|mem_fragmentation"
redis-cli CONFIG GET maxmemory

Scan for big keys: the usual culprit

A ZSET with a million members can make one full ZRANGE take seconds, queuing every command behind it. Use `redis-cli --bigkeys` for a low-overhead sampled view of the largest key per type; for precise sizes use `--memkeys` (Redis 6.0+) or MEMORY USAGE per key.

Note that --bigkeys samples via the SCAN cursor and is safe to run in production; never use KEYS * — it walks the whole keyspace in one blocking shot and is a classic outage maker.

redis-cli --bigkeys          # 采样找各类型最大 key(安全)
redis-cli MEMORY USAGE <key>  # 精确测量单个 key

Read the slowlog: which command drags

The built-in slowlog records commands above a threshold (default 10ms; 1ms captures more clues). `SLOWLOG GET 10` shows the last ten — note the command name, latency and key, then trace the calling code.

The high-latency leaderboard: KEYS, SMEMBERS on big sets, HGETALL on big hashes, full ZRANGE, FLUSHALL/FLUSHDB, long loops in Lua. Replace each: KEYS → SCAN, SMEMBERS → batched SSCAN, HGETALL → HMGET of just the needed fields.

redis-cli CONFIG SET slowlog-log-slower-than 1000   # 阈值降到 1ms
redis-cli SLOWLOG GET 10

Eviction policy: the switch for when memory fills

For caches choose allkeys-lru (evict least-recently-used across the keyspace) or allkeys-lfu (hotter precision, 4.0+); for data that must persist choose noeviction, but then scale and alert in advance — write failures are only a matter of time. The volatile-* family only evicts keys with TTLs; if your keys have none, it behaves as noeviction.

Verify immediately after changing policy: push a write burst and confirm no OOM errors. Eviction bursts may add slight latency (many DELs); lazyfree (4.0+) makes deletion asynchronous.

redis-cli CONFIG SET maxmemory-policy allkeys-lru
redis-cli CONFIG SET lazyfree-lazy-eviction yes

Persistence jitter: RDB fork and AOF fsync

BGSAVE forks a child, and the fork cost grows with instance size (page-table copy), briefly stalling the main thread — for periodic stalls on big instances check latest_fork_usec in INFO stats. AOF everysec is the balanced default; switching to always fsyncs every write and collapses throughput.

If Redis is a pure cache with rebuildable data, turning persistence off (save "", appendonly no) removes an entire class of jitter — the most common "free" optimization.

redis-cli INFO stats | grep latest_fork_usec   # fork 耗时(微秒)

Emergency and prevention checklist

Live-incident flow: ① locate the command via SLOWLOG; ② delete big keys with UNLINK (async) instead of DEL; ③ restart non-critical instances if needed. Prevention: ① shard big collections into smaller keys; ② replace full traversal with SCAN cursors; ③ cap maxmemory at 70% of physical RAM; ④ monitor used_memory, slowlog count and replica lag. Paste this checklist into the on-call handbook and Redis MTTR drops by half.

redis-cli UNLINK <big-key>   # 异步删除,不阻塞主线程