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:
| Command | Risk |
|---|---|
KEYS * | Scans every key in memory; blocks the server on large datasets |
FLUSHALL | Deletes all keys; instantaneous for small datasets, slower with many keys |
SORT | Complex sorting with BY and GET clauses can be CPU-intensive |
HGETALL on a giant Hash | Returns millions of fields in one response |
ZRANGE ... WITHSCORES on a huge Sorted Set | Serializes 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.
# 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 128Each 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:
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 msWith Pipeline:
Client: SET a 1 | SET b 2 | SET c 3
← OK | OK | OK (1 ms RTT)
# Total: 1 msPipeline in Practice
Most client libraries support Pipeline natively:
# 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.
# All sections
redis-cli INFO
# Specific sections
redis-cli INFO memory
redis-cli INFO stats
redis-cli INFO replication
redis-cli INFO commandstatsKey metrics to track:
| Section | Field | Meaning |
|---|---|---|
memory | used_memory | Total bytes allocated by Redis |
memory | used_memory_rss | Bytes the OS sees (includes fragmentation) |
stats | total_commands_processed | Throughput since startup |
stats | keyspace_hits / keyspace_misses | Cache hit ratio |
stats | evicted_keys | Keys removed due to maxmemory |
stats | rejected_connections | Clients turned away at maxclients |
Calculate cache efficiency:
redis-cli INFO stats | grep keyspace
# keyspace_hits:1234567
# keyspace_misses:12345
# Hit ratio ≈ hits / (hits + misses) = 99%Live Statistics
# One-line live metrics, updated every second
redis-cli --statOutput looks like:
------- data ------ --------------------- load -------------------- - child -
keys mem clients blocked requests connections
51234 1.23M 12 0 1234567 (+1234) 45Big Key Scanning
# 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 STATSMemory Analysis by Key Pattern
# Sample keys and estimate memory by pattern (can be slow)
redis-cli --memkeysLatency Monitoring
Redis includes a built-in latency doctor that analyzes possible causes of slowdowns.
# 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 forkCommon latency events:
command: A command took longer than the threshold.fork: Thefork()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:
# Iterate safely without blocking
SCAN 0 MATCH user:* COUNT 100
# Returns the next cursor and up to 100 matching keysSCAN 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%:
# redis.conf
unixsocket /run/redis/redis.sock
unixsocketperm 770What 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.