Redis Monitoring with Native Tools: A Hands-On Performance Audit
Five fields from a single INFO call answer most Redis health questions: used_memory (against maxmemory), mem_fragmentation_ratio, keyspace_hits, keyspace_misses and evicted_keys. If you can read those five and know what to do when each looks wrong, you're ahead of most teams running Redis in production. This guide is a complete Redis monitoring audit using nothing but redis-cli and the commands Redis ships with. No exporters, no dashboards, nothing to install.
Part one of this series covered the metrics that matter and what bad looks like: a collapsing cache hit ratio, memory fragmentation, eviction storms, O(N) commands blocking the event loop, big keys, hot keys and replication lag. This part is the audit itself, tool by tool, in the order you'd actually run it. Budget one to two hours for a first pass on a single production instance.
01Totals
INFO
Hit ratio, memory, evictions. Free to run, so it goes first.
02Commands
SLOWLOG
What blocked the event loop, with the exact arguments.
03Stalls
LATENCY
Forks, fsync and other spikes the slow log never sees.
04Attribution
MEMORY
Which keys and which overhead the totals are made of.
05Sweep
--bigkeys
A throttled keyspace scan, ideally on a replica.
One ground rule before you start: Redis runs commands on a single thread. Every diagnostic you run competes with production traffic, so prefer the sampled and throttled variants below, and run the heavier passes on a replica where you have one. On managed services (ElastiCache, Azure Cache for Redis), some CONFIG SET calls are blocked; make those changes in the provider's parameter group or configuration screen instead.
Tool 1: INFO, the five numbers and then the rest
INFO is cheap to run and covers hit ratio, memory and eviction behavior in one call (INFO docs).
Step 1: compute your cache hit ratio
redis-cli INFO stats | grep -E 'keyspace_hits|keyspace_misses'
Hit ratio is keyspace_hits / (keyspace_hits + keyspace_misses). As a one-liner (the tr strips the carriage returns INFO puts at the end of each line):
redis-cli INFO stats | tr -d '\r' | awk -F: '/^keyspace_hits/{h=$2} /^keyspace_misses/{m=$2} END{printf "hit ratio: %.4f\n", h/(h+m)}'
How to read it. For a cache workload, healthy is usually above 0.90. Below 0.80, your database is absorbing traffic Redis was supposed to shield it from. Two caveats. These counters accumulate since the last restart, so on a long-lived instance the ratio smooths over recent regressions: sample twice, 10 minutes apart, and diff the raw counters for a current-rate view. And if Redis is your primary datastore rather than a cache, a "low" ratio may just be your access pattern.
Step 2: read the memory section
redis-cli INFO memory | grep -E 'used_memory_human|used_memory_rss_human|maxmemory_human|mem_fragmentation_ratio|maxmemory_policy'
How to read it. mem_fragmentation_ratio is RSS divided by used_memory. Between 1.0 and 1.5 is normal. Above 1.5, the allocator is holding memory Redis freed but can't return, which is common after mass deletions or heavy key churn. Below 1.0 is worse: the OS has swapped Redis memory to disk, and latency is about to fall off a cliff. If fragmentation is high and you're on a modern build with jemalloc, active defragmentation fixes it without a restart:
redis-cli CONFIG SET activedefrag yes
Also check used_memory against maxmemory. If maxmemory is 0 (unset) on a cache instance, that's a finding on its own. Redis will grow until the OS OOM killer decides otherwise.
Step 3: evictions and expirations
redis-cli INFO stats | grep -E 'evicted_keys|expired_keys'
redis-cli INFO keyspace
How to read it. expired_keys climbing is healthy: TTLs doing their job. evicted_keys climbing means demand exceeds maxmemory and Redis is throwing data out under pressure. On a cache, sustained evictions usually show up as the hit-ratio drop you measured in step 1. INFO keyspace shows keys and expires per database. If expires is a small fraction of keys on a cache workload, a large share of your keyspace has no TTL and will never leave on its own. That's your first remediation ticket.
Tool 2: SLOWLOG, the commands stalling the event loop
The slow log records commands whose execution exceeded a threshold. It excludes network time and queueing, so anything in here blocked every other client while it ran (SLOWLOG docs).
Set a sane window first. The threshold is in microseconds (default 10000, or 10 ms), and the log keeps 128 entries by default:
redis-cli CONFIG SET slowlog-log-slower-than 10000
redis-cli CONFIG SET slowlog-max-len 1024
Then, after production traffic has run for a while:
redis-cli SLOWLOG GET 25
redis-cli SLOWLOG LEN
How to read it. Each entry shows an ID, a timestamp, the execution time in microseconds, the command with its arguments and, on Redis 4.0 and later, the client address and name. That last part matters: it tells you which service sent the command. You're hunting the usual suspects from part one: KEYS * (never in production; use SCAN), SMEMBERS, LRANGE 0 -1 or HGETALL against large collections, SORT without limits, and huge MGET or pipeline batches. A 50 ms entry means every client stalled for 50 ms. The fix is usually to replace full-collection reads with SSCAN, HSCAN or bounded LRANGE windows, and to break monolithic collections into smaller keys. Run SLOWLOG RESET after remediation so your next audit starts clean.
Tool 3: LATENCY, when slowness isn't a slow command
Not all latency comes from expensive commands. Forks for RDB snapshots, AOF fsync stalls, transparent huge pages and expire cycles all cause spikes the slow log never sees. Redis has a built-in latency monitor for exactly this, and it's off by default (latency monitor docs).
Enable it with a threshold in milliseconds:
redis-cli CONFIG SET latency-monitor-threshold 100
After it has watched some traffic:
redis-cli LATENCY LATEST
redis-cli LATENCY HISTORY command
redis-cli LATENCY DOCTOR
How to read it. LATENCY LATEST lists each event class with its latest and all-time maximum spike. LATENCY HISTORY takes an event name and returns timestamped samples: command covers slow command execution, fork covers snapshot and replication forks, aof-fsync-always covers disk stalls. LATENCY DOCTOR is genuinely useful. It analyzes the samples and writes plain-language advice; it will tell you, for example, if fork latency tracks instance memory size, or to check your THP settings. If fork events dominate on a large instance, your options are less memory per instance, persistence moved to a replica, or accepting the spike window.
Tool 4: MEMORY USAGE and MEMORY DOCTOR, per-key accounting
INFO memory gives totals. The MEMORY subcommands attribute them (MEMORY USAGE docs).
# Bytes for one key, sampling all elements (SAMPLES 0 = exact, costs more)
redis-cli MEMORY USAGE user:12345:sessions SAMPLES 0
# Allocator-level breakdown: overhead, dataset, per-db, fragmentation
redis-cli MEMORY STATS
# Built-in advisor
redis-cli MEMORY DOCTOR
How to read it. Use MEMORY USAGE to price the specific keys your slow log and big-key scan point at. A "small" hash that turns out to hold 40 MB explains a lot of HGETALL pain. In MEMORY STATS, compare overhead.total with dataset.bytes. High overhead relative to data usually means millions of tiny keys, where per-key metadata dominates and folding fields into hashes saves real memory. MEMORY DOCTOR gives blunt advice on fragmentation and allocator health, a quick check that your reading of the numbers matches Redis's own.
Tool 5: redis-cli --bigkeys and --hotkeys, the keyspace sweep
The redis-cli scanning modes walk the keyspace with SCAN, so they don't block the server the way KEYS would. They still generate load, so throttle them and prefer a replica (redis-cli docs):
# Largest key per type, by element count; -i 0.1 sleeps 100 ms per 100 SCAN calls
redis-cli --bigkeys -i 0.1
# Largest keys by actual bytes (uses MEMORY USAGE under the hood)
redis-cli --memkeys -i 0.1
# Most-accessed keys; needs an LFU eviction policy (next section)
redis-cli --hotkeys
How to read it. --bigkeys reports the biggest key per type by element count. --memkeys is the better signal because it measures bytes. Any single key holding hundreds of MB, or a collection with millions of members, is a finding: big keys serialize slowly, block on delete (use UNLINK, not DEL) and stall replication. --hotkeys only works when maxmemory-policy is one of the LFU policies, because it reads the access-frequency counters LFU maintains. On LRU it tells you so and exits. A single hot key taking a large share of traffic is your sharding or client-side caching candidate, before the next traffic spike finds it for you.
Choosing a maxmemory-policy
Everything above feeds one configuration decision: what Redis does at the memory ceiling (eviction docs).
01Pure cache
allkeys-lfu
Every key can be refetched, so evict the least-used ones from anywhere.
02Mixed
volatile-lfu
TTLs mark the cache keys. Keys without a TTL are never evicted.
03Datastore
noeviction
Losing a key is data loss. Writes fail at the ceiling, so plan capacity.
redis-cli CONFIG GET maxmemory-policy
redis-cli CONFIG SET maxmemory-policy allkeys-lfu
redis-cli CONFIG REWRITE # persist the change to redis.conf
The full decision table:
| Workload | Policy |
|---|---|
| Pure cache, all keys refetchable | allkeys-lfu (or allkeys-lru before 4.0) |
| Cache plus must-keep keys, TTLs mark the cache | volatile-lfu / volatile-lru |
| Expiry-ordered eviction makes semantic sense | volatile-ttl |
| Primary datastore, losing keys is data loss | noeviction plus real capacity planning |
Two traps. noeviction on a cache means writes start failing with OOM errors at the ceiling instead of quietly evicting: fine for a datastore, an outage for a cache. And any volatile-* policy with few TTL'd keys (check INFO keyspace from step 3) has almost nothing it's allowed to evict, so it behaves like noeviction at the worst possible moment.
Which tool answers which question
| Question | Native tool | What it can't tell you |
|---|---|---|
| Is the cache doing its job? | INFO stats | When it stopped, since counters are lifetime totals |
| Where did the RAM go? | INFO memory, MEMORY STATS | Which deploy started the growth |
| What blocked the event loop? | SLOWLOG GET | Anything older than the last 128 (or 1,024) entries |
| Was it a fork or the disk? | LATENCY HISTORY | Anything older than 160 samples per event |
| Which keys are the problem? | --memkeys, MEMORY USAGE | Which code path writes them |
| Is the eviction policy right? | CONFIG GET, INFO keyspace | What will break when you change it |
Native tooling answers what and where well. The right-hand column, when and why, is what turns a 20-minute check into a two-hour investigation.
Where the DIY audit stops
Run this audit. It will find real problems. But be honest about its structural limits.
01Limit
A snapshot
The audit describes today. An unbounded HGETALL shipped next sprint waits for the next audit.
02Limit
Evidence rolls
SLOWLOG and LATENCY keep a fixed number of entries. A busy night rotates them out.
03Still a person
No follow-through
Every finding becomes a ticket, and cache hygiene loses priority fights.
It's a snapshot. Hit ratio, fragmentation and eviction counters describe today. The deploy that ships an unbounded HGETALL next sprint won't announce itself. You'll find it on the next audit, weeks after users felt it.
The counters forget and the logs roll. SLOWLOG holds a fixed number of entries and LATENCY HISTORY keeps 160 samples per event, so a busy night can rotate the evidence out before you look. Cumulative INFO counters blur regressions into long-run averages unless someone keeps diffing them.
Detection isn't remediation. Nothing above adds a TTL strategy, refactors a big key or reshards a hot one. Every finding becomes a ticket, and cache-hygiene tickets lose priority fights until the day the hit ratio drops and the database falls over.
What comes next
Everything in this guide can run on a schedule instead of on a good day. CloudThinker connects to Redis with a read-only ACL user and watches hit ratio, fragmentation, evictions and the slow log continuously. It proposes TTL strategies, eviction-policy changes and big-key refactors with the evidence attached, and every change waits for your approval. That's part three of this series.
Related reading
- Redis monitoring: 6 metrics that decide if your cache is helping
- Automating Redis monitoring with an AI agent: from graphs to fixes
- Hands-on PostgreSQL performance tuning with native tools only
Or see your own instance's findings first: try CloudThinker free with 100 premium credits and no card required.
