Redis — SLOWLOG Filled with Big-Key Operations
Diagnose Redis latency spikes from big keys and blocking commands via SLOWLOG and LATENCY HISTORY, find the keys with memory usage analysis, and break them up.
Symptoms
- SLOWLOG entries show KEYS, SMEMBERS on huge sets, or HGETALL on million-field hashes taking hundreds of milliseconds
- Other clients see periodic latency spikes synchronized with those commands
- LATENCY HISTORY shows a sawtooth aligned with report or batch jobs
Possible causes
- A set, hash, or list grew to millions of members, and whole-structure commands process it atomically
- A reporting job runs KEYS * or a wide SMEMBERS during business hours
- A single key stores what should be many keys (user-bucketed data in one hash)
- Deletion of a big key with DEL blocks the main thread for the whole free()
Diagnosis steps
Read the slow log entries
redis-cli slowlog get 10 --no-rawInterpretation
The command name and duration identify the operation class; the client address maps to the calling service.
Find the offending keys
redis-cli --bigkeys -i 0.01; redis-cli memory usage report:monthlyInterpretation
Sampling bigkeys plus memory usage per suspect quantifies which keys are structurally oversized.
Confirm the latency correlation
redis-cli latency history command; redis-cli latency latestInterpretation
Latency events aligned with the slowlog timestamps tie client-observed spikes to the specific commands.
Root cause
Redis executes commands on a single thread, so one command processing millions of elements delays every other client for its full duration. Big keys make that O(n) cost large; blocking whole-structure operations (KEYS, SMEMBERS, HGETALL, DEL of a big key) turn structural bloat into periodic latency spikes.
Fix
Replace whole-structure reads with cursor iteration (SSCAN, HSCAN), bucket big structures into many small keys with predictable prefixes, use UNLINK instead of DEL for large keys, and replace KEYS with SCAN in application code permanently.
Risk note
SSCAN with a changing key can yield duplicates or misses; make scan-based jobs idempotent and tolerant of mild drift.
Verify the fix
After the restructure, SLOWLOG stays empty during the same workload, LATENCY HISTORY shows no command events, and client p99 returns to baseline.
Symptom walkthrough
The slow log names the operation and its cost:
redis-cli slowlog get 3
1) 1) (integer) 14
2) (integer) 1729584000
3) (integer) 384112 <- 384 ms
4) 1) "HGETALL"
2) "report:2026-10"
One command, 384 milliseconds of single-threaded time — every other client waited behind it. Find the structural culprit:
redis-cli --bigkeys -i 0.01
# Sampled 100000 keys
# Biggest hash found 'report:2026-10' has 3120000 fields
Three million fields in one hash is the whole disease. The client address in the entry maps to the service that can stop asking for it wholesale.
Fix in practice
Cursor-based reads replace whole-structure operations:
cursor = 0
while True:
cursor, batch = r.hscan("report:2026-10", cursor=cursor, count=500)
process(batch)
if cursor == 0:
break
Bucket the structure at write time so no key grows unbounded:
# one hash per day instead of one hash per month:
key = f"report:{day:%Y-%m-%d}"
r.hset(key, field, value)
And the two permanent swaps: UNLINK instead of DEL for large keys (background free), and SCAN instead of KEYS anywhere in application code — KEYS in a cron is a scheduled outage.