Performance and Monitoring

Introduction

Redis is fast by default, but fast is not the same as free. A poorly chosen command, an oversized key, or a missing index pattern can turn a sub-millisecond operation into a multi-second freeze. This chapter covers the fundamentals of Redis performance, the Pipeline technique for batching commands, and the built-in monitoring tools you need to spot problems before they become outages.

Prerequisites

  • Familiarity with basic Redis commands and data types
  • A running Redis server and access to redis-cli
  • Understanding of the single-threaded event loop (see What Is Redis)

Performance Foundations

The Cost of a Single Command

Because Redis is single-threaded, every command must complete before the next one starts. Most simple commands—GET, SET, HGET, LPUSH—execute in microseconds. Problems arise with commands that scan, sort, or delete large structures:

CommandRisk
KEYS *Scans every key in memory; blocks the server on large datasets
FLUSHALLDeletes all keys; instantaneous for small datasets, slower with many keys
SORTComplex sorting with BY and GET clauses can be CPU-intensive
HGETALL on a giant HashReturns millions of fields in one response
ZRANGE ... WITHSCORES on a huge Sorted SetSerializes and transmits massive payloads

Tip

The O(N) Rule

Before running any command, ask: how does its complexity scale with the size of the data? Check the Redis documentation for Big-O notation. If it is O(N) and N might be millions, find an alternative.

Avoiding Slow Queries

Redis provides a slow log that records commands exceeding a configurable execution threshold.

bash
# Show the 10 slowest recent commands
SLOWLOG GET 10
 
# Get the current slow-log threshold in microseconds (default 10000 = 10ms)
CONFIG GET slowlog-log-slower-than
 
# Lower the threshold to 1 ms for debugging
CONFIG SET slowlog-log-slower-than 1000
 
# Limit the log length
CONFIG SET slowlog-max-len 128

Each entry includes the timestamp, duration, command, and client IP. Review this log weekly in production.

Pipeline

Network latency is often the biggest bottleneck in Redis, not the server itself. If your round-trip time is 1 ms and you send 1000 commands one by one, you spend a full second waiting for the network. Pipeline batches multiple commands into a single request and reads all replies at once.

How Pipeline Works

Without Pipeline:

plaintext
Client: SET a 1
        ← OK (1 ms RTT)
Client: SET b 2
        ← OK (1 ms RTT)
Client: SET c 3
        ← OK (1 ms RTT)
# Total: 3 ms

With Pipeline:

plaintext
Client: SET a 1 | SET b 2 | SET c 3
        ← OK | OK | OK (1 ms RTT)
# Total: 1 ms

Pipeline in Practice

Most client libraries support Pipeline natively:

python
# Python example with redis-py
import redis
 
r = redis.Redis()
pipe = r.pipeline()
 
for user_id in range(1000):
    # Queue commands; nothing is sent yet
    pipe.hset(f"user:{user_id}", "synced", "true")
 
# Send all 1000 commands in one batch
pipe.execute()

Warning

Pipeline Is Not a Transaction

Commands in a Pipeline are sent together, but they execute individually on the server. Other clients can interleave their commands between yours. If you need atomicity, use MULTI / EXEC or a Lua script inside the Pipeline.

Pipeline Limitations

  • Very large Pipelines consume memory on both client and server while buffering commands and replies.
  • If the server crashes mid-Pipeline, some commands may have executed and others not. There is no atomic rollback.
  • A good rule of thumb is to batch commands in groups of 100 to 10,000, depending on command size and available memory.

Monitoring and Diagnostics

The INFO Command

INFO is the Swiss Army knife of Redis monitoring. It returns sections covering every subsystem.

bash
# All sections
redis-cli INFO
 
# Specific sections
redis-cli INFO memory
redis-cli INFO stats
redis-cli INFO replication
redis-cli INFO commandstats

Key metrics to track:

SectionFieldMeaning
memoryused_memoryTotal bytes allocated by Redis
memoryused_memory_rssBytes the OS sees (includes fragmentation)
statstotal_commands_processedThroughput since startup
statskeyspace_hits / keyspace_missesCache hit ratio
statsevicted_keysKeys removed due to maxmemory
statsrejected_connectionsClients turned away at maxclients

Calculate cache efficiency:

bash
redis-cli INFO stats | grep keyspace
# keyspace_hits:1234567
# keyspace_misses:12345
# Hit ratio ≈ hits / (hits + misses) = 99%

Live Statistics

bash
# One-line live metrics, updated every second
redis-cli --stat

Output looks like:

plaintext
------- data ------ --------------------- load -------------------- - child -
keys       mem      clients blocked requests            connections
51234      1.23M    12      0       1234567 (+1234)     45

Big Key Scanning

bash
# Sample the keyspace and report the largest keys by type
redis-cli --bigkeys
 
# Memory usage of a specific key
redis-cli MEMORY USAGE user:1000:history
 
# Human-readable breakdown of memory internals
redis-cli MEMORY STATS

Memory Analysis by Key Pattern

bash
# Sample keys and estimate memory by pattern (can be slow)
redis-cli --memkeys

Latency Monitoring

Redis includes a built-in latency doctor that analyzes possible causes of slowdowns.

bash
# Enable latency monitoring (if not already on)
CONFIG SET latency-monitor-threshold 100
 
# Check for known latency spikes
LATENCY DOCTOR
 
# List available latency events
LATENCY HISTORY command
LATENCY HISTORY fork

Common latency events:

  • command: A command took longer than the threshold.
  • fork: The fork() system call for RDB or AOF rewrite was slow.
  • aof-write: Writing to the AOF file blocked.
  • rdb-unlink-temp-file: Deleting the temporary RDB file after a background save.

FAQ

How many commands per second can Redis handle?

On modest hardware, 100,000+ simple commands per second is typical. With Pipelines and local Unix sockets, that number can exceed 1,000,000. Actual throughput depends on command complexity, payload size, and network latency.

Is there a command to list all keys?

KEYS * exists but should never be used in production. It scans the entire keyspace and blocks the server. Use SCAN instead:

bash
# Iterate safely without blocking
SCAN 0 MATCH user:* COUNT 100
# Returns the next cursor and up to 100 matching keys

SCAN is incremental: each call returns a cursor you pass to the next call until it returns 0.

Why is my HGETALL so slow?

The Hash probably has too many fields. There is no hard limit, but once you exceed a few thousand fields, serialization and network transfer become noticeable. Shard large Hashes by a secondary key (for example, user:42:profile:a-m and user:42:profile:n-z).

Should I use Unix sockets instead of TCP?

If the client and server run on the same machine, yes. Unix sockets eliminate TCP overhead and can improve throughput by 10–30%:

conf
# redis.conf
unixsocket /run/redis/redis.sock
unixsocketperm 770

What is UNLINK and why is it faster than DEL?

DEL removes a key synchronously, which can freeze the event loop for large keys. UNLINK deletes the key from the keyspace immediately and reclaims memory asynchronously in a background thread. Prefer UNLINK unless you are certain the key is small.

Can I disable persistence to improve performance?

Yes, but only if Redis holds purely ephemeral data. Set save "" and appendonly no. This removes fork and disk I/O overhead, but a restart wipes everything.