The symptom
The application reports Redis timeouts or a p99 that has jumped from under a millisecond to tens or hundreds of milliseconds. Average latency often looks fine. CPU on the host may look low, because Redis executes commands on one main thread: a single slow command blocks every other client while it runs.
Work through the checks in order. Most incidents end at step 2 or 3.
1. Measure from the client side first
Confirm the latency is real and where it starts. Run these from an application host, not from the Redis server itself.
# Continuous PING round trips, in milliseconds
redis-cli -h redis01 -p 6379 --latency
# Same, but a new line of min/max/avg every 15 seconds - good for spotting spikes
redis-cli -h redis01 -p 6379 --latency-history -i 15
Then, on the Redis host, check how much latency the machine itself adds (virtualisation, noisy neighbours):
# Runs for 60 seconds; do not run on a host that is already CPU-bound
redis-cli --intrinsic-latency 60
If intrinsic latency alone is several milliseconds, the problem is the VM or host, not Redis. If --latency from the app host is high but from the Redis host is low, look at the network, TLS, or connection pools.
2. Read the slow log
The slow log records commands whose execution time (not network time) exceeded a threshold.
redis-cli CONFIG GET slowlog-log-slower-than # microseconds; default 10000 (10 ms)
redis-cli CONFIG GET slowlog-max-len # entries kept in memory; default 128
redis-cli SLOWLOG LEN
redis-cli SLOWLOG GET 20
Each entry shows an ID, Unix timestamp, duration in microseconds, the command with its arguments and (Redis 4+) the client address and name. During an investigation, lower the threshold and keep more entries:
redis-cli CONFIG SET slowlog-log-slower-than 5000
redis-cli CONFIG SET slowlog-max-len 1024
Typical offenders:
| Slow log shows | Why it's slow | Fix |
|---|---|---|
KEYS * | Walks every key, O(N) | Replace with SCAN with a COUNT |
HGETALL, SMEMBERS, LRANGE 0 -1, ZRANGE 0 -1 on big keys | Returns the whole collection | Page with HSCAN / SSCAN / ZSCAN or ranged reads; split the key |
DEL on a key with millions of elements | Freeing memory synchronously | Use UNLINK; enable lazy free (below) |
SORT, SUNION, ZUNIONSTORE on large sets | O(N) or worse | Precompute, or move the work to the application |
EVAL / EVALSHA / FCALL | Long Lua script blocks the server | Make scripts bounded; batch smaller |
FLUSHALL / FLUSHDB | Synchronous flush | FLUSHALL ASYNC |
To see which commands cost the most overall, not just the slowest single calls:
redis-cli INFO commandstats | sort -t= -k4 -rn | head
# cmdstat_hgetall:calls=...,usec=...,usec_per_call=...
Redis 7 adds per-command percentiles:
redis-cli INFO latencystats
Warning
A single
KEYS *on a 50-million-key instance can block for seconds and trigger Sentinel or cluster failover, because the node stops answering health checks. Rename or disableKEYSfor application users with ACLs:ACL SETUSER app -keys.
3. Turn on the latency monitor
The slow log only sees commands. Forks, AOF fsyncs, expiry and eviction cycles block the server too, and the latency monitor records them.
# Record any event that takes longer than 10 ms (default 0 = disabled)
redis-cli CONFIG SET latency-monitor-threshold 10
# After the next spike:
redis-cli LATENCY LATEST
redis-cli LATENCY DOCTOR
redis-cli LATENCY HISTORY fork
LATENCY LATEST lists each event name with the latest and maximum latency. LATENCY DOCTOR gives a plain-English analysis with advice. Event names vary by version; common ones are below.
| Event | What it means | Where to look next |
|---|---|---|
command / fast-command | Slow commands | Step 2 |
fork | fork() for RDB save or AOF rewrite | Step 4 |
aof-fsync-always, aof-write, aof-write-pending-fsync | Disk is slow to accept AOF writes | Step 5 |
expire-cycle | Many keys expiring at the same moment | Step 6 |
eviction-cycle, eviction-del | Evicting under maxmemory pressure | Step 6 |
Reset the counters after you change something so you can see the effect: redis-cli LATENCY RESET.
4. Fork latency from RDB and AOF rewrite
Redis forks a child for BGSAVE and BGREWRITEAOF. The fork copies page tables, and the main thread is blocked while it happens. Large datasets on some VMs can take hundreds of milliseconds.
redis-cli INFO stats | grep latest_fork_usec
redis-cli INFO persistence | egrep "rdb_bgsave_in_progress|aof_rewrite_in_progress|rdb_last_bgsave_time_sec"
Fixes:
Disable transparent huge pages. With THP on, copy-on-write after the fork copies 2 MB pages instead of 4 KB pages, which causes long stalls and memory growth. Redis logs a warning at startup when THP is enabled.
cat /sys/kernel/mm/transparent_hugepage/enabled echo never | sudo tee /sys/kernel/mm/transparent_hugepage/enabledMake it persistent with your distribution's method (tuned profile, systemd unit or kernel boot parameter
transparent_hugepage=never).Schedule snapshots less often, or take them only on a replica.
Split a very large instance into shards so each fork is smaller.
On ElastiCache and Azure the fork happens during backups and replica syncs. Move the backup window to a quiet period and keep memory headroom (reserved-memory-percent on ElastiCache, maxmemory-reserved on Azure).
5. AOF fsync on slow disks
redis-cli CONFIG GET appendonly
redis-cli CONFIG GET appendfsync
redis-cli INFO persistence | grep aof_delayed_fsync
aof_delayed_fsync increasing means the background fsync could not keep up and the main thread had to wait. Options:
appendfsync everysecinstead ofalways(up to about one second of writes at risk on a crash).no-appendfsync-on-rewrite yesto skip fsync while a rewrite is running (more data at risk during the rewrite).- Put the AOF on faster storage, away from other heavy I/O.
Caution
Relaxing fsync settings trades durability for latency. Only do it if the data can be rebuilt or the RPO agreed for this instance allows it.
6. Mass expiry, eviction and big deletes
Thousands of keys created with the same TTL expire together, and the active expiry cycle works harder. Large keys freed during eviction or DEL also block.
redis-cli INFO stats | egrep "expired_keys|evicted_keys|lazyfree_pending_objects"
redis-cli CONFIG GET "lazyfree-*"
Add jitter to TTLs in the application (for example base TTL plus a random 0–10%).
Turn on lazy freeing so large values are freed on a background thread:
redis-cli CONFIG SET lazyfree-lazy-eviction yes redis-cli CONFIG SET lazyfree-lazy-expire yes redis-cli CONFIG SET lazyfree-lazy-server-del yes redis-cli CONFIG SET lazyfree-lazy-user-del yes # Redis 6+: DEL behaves like UNLINKFind and split big keys:
redis-cli --bigkeys, thenMEMORY USAGE <key>.
7. Clients and connections
Connection storms and huge replies also show up as latency.
redis-cli INFO clients # connected_clients, blocked_clients
redis-cli INFO stats | egrep "total_connections_received|rejected_connections"
# Clients with the largest output buffers (omem, bytes) first
redis-cli CLIENT LIST | awk '{o=0; for(i=1;i<=NF;i++){ if($i ~ /^omem=/){split($i,a,"="); o=a[2]} } print o, $0}' | sort -rn | head
High total_connections_received growth means clients connect per request. Use connection pooling and TLS session reuse. A large omem (output buffer memory) points at a client reading a huge reply or a slow Pub/Sub subscriber.
Managed services
| Service | Metrics to check | Notes |
|---|---|---|
| Amazon ElastiCache | EngineCPUUtilization, StringBasedCmdsLatency, KeyBasedCmdsLatency, CurrConnections, NewConnections | EngineCPUUtilization is the Redis main thread; CPUUtilization is the whole host |
| Azure Cache for Redis / Azure Managed Redis | Server Load, CPU, Connected Clients, Cache Latency | Metric names vary by tier and version; check the Metrics blade |
On managed services SLOWLOG usually works. Some CONFIG and LATENCY settings are set through the parameter group or advanced settings instead of CONFIG SET; check what your service allows.
Prevent it next time
- Keep
slowlog-log-slower-thanat 10 ms or lower and shipSLOWLOG GEToutput to your logging tool on a schedule. - Leave
latency-monitor-thresholdset (for example 50 ms) in production; the overhead is small. - Block
KEYS,FLUSHALLandFLUSHDBfor application users with ACLs. - Disable THP on every self-managed Redis host as part of the build.
- Alert on p99 latency from the client,
EngineCPUUtilization(or Server Load) above about 80%, and risingevicted_keys. - Review
--bigkeysoutput monthly and set a size limit for collections in the application.