# Diagnose a slow Redis instance

A read-only walkthrough for finding why a Redis is slow. Every command here reads,
nothing here mutates. Run them from any host that can reach the instance with
`redis-cli`; if you have RKB, the same numbers appear in the Monitor's Health and
Capacity cards.

## 1. Confirm the symptom in one place

A slow instance shows up as one of: rising p99 client latency, climbing
`instantaneous_ops_per_sec` against unchanged traffic, blocked clients, or replicas
falling behind. Pull a snapshot first so the rest of the runbook has a baseline:

```sh
redis-cli -h $HOST -p $PORT INFO all > info.txt
redis-cli -h $HOST -p $PORT CLIENT LIST > clients.txt
redis-cli -h $HOST -p $PORT MEMORY STATS > memstats.txt
```

If RKB is set up against this host, the same data is in the live pull and the
Health card already grades it. Use the CLI version when you want to attach files
to a ticket.

## 2. Is the workload latency-bound or throughput-bound?

```sh
redis-cli -h $HOST -p $PORT --latency-history
redis-cli -h $HOST -p $PORT --intrinsic-latency 60
```

`--latency-history` reports the cost of a `PING` round-trip from your host every
second. `--intrinsic-latency` measures how long the server's event loop is blocked
running a tight loop on the server itself, with no client traffic. The gap between
the two tells you whether the slowness is on the wire or on the server.

If intrinsic latency is healthy (sub-millisecond on modern hardware) but client
latency is high, the bottleneck is the network, the client pool, or a TLS
handshake storm. If intrinsic latency is itself high, the server is busy and the
rest of the runbook applies.

## 3. Look at what is actually running right now

```sh
redis-cli -h $HOST -p $PORT CLIENT LIST | head -50
redis-cli -h $HOST -p $PORT SLOWLOG GET 25
```

`CLIENT LIST` shows every connection with the command it is running (`cmd=`), how
long the connection has been open (`age=`), and how much memory its query buffer
is using (`qbuf=`). A handful of clients with very large `qbuf` or stuck on the
same `cmd=` for many seconds is the smoking gun for a slow command.

`SLOWLOG GET 25` returns the last 25 commands that exceeded
`slowlog-log-slower-than` (10 ms by default). Look for repeat offenders:

- `KEYS *`, `SMEMBERS` on a large set, `HGETALL` on a large hash, `LRANGE 0 -1`
  on a long list. These are O(N) commands that block the single thread.
- `EVAL` scripts with no upper bound on iteration.
- `DEBUG SLEEP` left in by a test (this happens).
- Module commands with surprising costs (`FT.SEARCH` with no LIMIT, `JSON.GET`
  with a deep path, `TS.MRANGE` over a wide window).

The fix is almost always to replace the offending pattern: `SCAN` instead of
`KEYS`, paginated `HSCAN` / `SSCAN`, `LRANGE` with bounded indices.

## 4. Memory pressure (the second usual suspect)

```sh
redis-cli -h $HOST -p $PORT INFO memory
```

Look at three numbers:

- `used_memory` against `maxmemory`. Approaching the limit means evictions start
  to bite, and on `noeviction` writes get rejected with OOM.
- `mem_fragmentation_ratio`. Above 1.5 means the allocator is wasting RAM; turn
  on `activedefrag yes` if it is not already. Below 1.0 usually means the
  process is swapping, which is catastrophic for Redis latency.
- `evicted_keys` against the last sample. If it is climbing, the cache is too
  small for the working set; either raise `maxmemory` or shed cold keys.

If you have RKB pointed at this host, the Capacity card produces a recommended
`maxmemory` value with the formula shown next to it, and the Health card flags
fragmentation, eviction policy, and connected-clients pressure with their
thresholds.

## 5. Persistence and fork timing

```sh
redis-cli -h $HOST -p $PORT INFO persistence | grep -E 'rdb_|aof_'
```

A long `latest_fork_usec` (anything over a few hundred milliseconds) means the
fork that drives RDB snapshots or AOF rewrites stalls the server while the
kernel copies the page table. On a host with a lot of writes and a large
dataset this is the dominant tail-latency cause.

`aof_current_size` divided by `aof_base_size` over time is the AOF write
amplification; if it grows quickly, `appendfsync everysec` is correct but the
disk is the ceiling.

## 6. Replication lag

```sh
redis-cli -h $HOST -p $PORT INFO replication
```

On a primary, `connected_slaves` should equal the planned count. Each replica
shows `lag=<seconds>` and `state=online`. Anything other than `online` or a lag
that does not return to zero means the link is throttled (network, disk, or the
primary's write rate exceeds the replica's apply rate).

On a replica, `master_link_status:up` and `master_last_io_seconds_ago` under 10
are the healthy values. A flapping link makes the primary do partial resyncs
constantly, which costs memory on the primary's replication backlog.

## 7. Module-specific (Redis 8 with the bundled data modules)

If the slow command is a Search, JSON, TimeSeries, Bloom, or Vector operation,
each module has its own diagnostic surface:

```sh
redis-cli FT._LIST                       # the Search indexes that exist
redis-cli FT.INFO myidx                  # cardinality and the index size
redis-cli TS.INFO myseries               # TimeSeries chunk count and retention
redis-cli JSON.DEBUG MEMORY mykey        # the size of a JSON document
```

`FT.INFO`'s `inverted_sz_mb` field is the most common surprise: a Search index
on a high-cardinality field grows fast and can dominate memory.

## 8. Last-resort triggers

If after all the above the server is still slow and not actionable, capture a
profile and move on to a controlled failover:

```sh
redis-cli -h $HOST -p $PORT DEBUG SLEEP 0.0                 # sanity ping
redis-cli -h $HOST -p $PORT --latency-dist 30 > dist.txt    # latency histogram
```

If this instance is part of a Sentinel or Cluster topology, the safest next step
is to fail over to a replica and look at the old primary offline. The
`CLIENT PAUSE` window during a Sentinel failover is on the order of seconds,
which is almost always better than letting a degraded primary continue.

## What the RKB tool gives you for free here

If you point the tool at this instance with a read-only pull:

- The Health card flags memory %, eviction policy at limit, replication link,
  clients near maxclients, fragmentation, eviction count, RDB/AOF status, and
  loading state, all with thresholds.
- The Capacity card prints dataset size, recommended `maxmemory`, fork-on-save
  headroom, persistence disk per shard, throughput vs the 175k/s per-process
  ceiling, and the cache-loss multiplier at the current hit rate, every number
  with the formula next to it.
- The Topology card shows replication and cluster shape, so a missing replica
  or a CLUSTERDOWN is visible at a glance.

The CLI commands above are what the tool issues under the hood, so the answers
line up either way.
