Clustering and client libraries in applications
Hash slots, slot-aware clients, multi-key limits and hash tags, resharding, connection pooling, retries and idempotency.
Slots and the routing model
redis-cli --cluster create 10.0.0.1:6379 10.0.0.2:6379 10.0.0.3:6379 \
10.0.0.4:6379 10.0.0.5:6379 10.0.0.6:6379 --cluster-replicas 1
redis-cli -c -p 6379 CLUSTER INFO
redis-cli -c -p 6379 CLUSTER SHARDS
redis-cli -c -p 6379 CLUSTER KEYSLOT user:42
redis-cli -c -p 6379 CLUSTER COUNTKEYSINSLOT 5460
# reshard: move 100 slots to another node, interactively
redis-cli --cluster reshard 10.0.0.1:6379
# rebalance automatically, and check the cluster afterwards
redis-cli --cluster rebalance 10.0.0.1:6379 --cluster-use-empty-masters
redis-cli --cluster check 10.0.0.1:6379- There are 16384 slots. A key routes to
CRC16(key) mod 16384, and the client must send the command to the node owning that slot. - MOVED means the slot lives elsewhere permanently and the client must update its map; ASK means it moved temporarily during a migration and the client should send the command to the named node once.
- A client that does not handle MOVED and ASK is not cluster-capable. Silent misrouting produces
CROSSSLOTerrors and lost writes. - Multi-key commands work only when all keys hash to the same slot - which is what hash tags provide:
{user:42}:profileand{user:42}:cartshare a slot.
⚠️
A single hash tag used across the whole application pins every key to one slot, turning a cluster into a single node with extra hops. Tag by the entity you actually need to operate on atomically, and only for those keys.
Client configuration
from redis.cluster import RedisCluster, ClusterNode
from redis.backoff import ExponentialBackoff
from redis.retry import Retry
client = RedisCluster(
startup_nodes=[
ClusterNode("10.0.0.1", 6379),
ClusterNode("10.0.0.2", 6379),
],
decode_responses=True,
max_connections=50, # per node
socket_timeout=1.0,
socket_connect_timeout=1.0,
retry=Retry(ExponentialBackoff(cap=0.5, base=0.05), retries=3),
retry_on_error=[ConnectionError, TimeoutError],
)
# a multi-key operation needs a shared hash tag
with client.pipeline() as pipe:
pipe.hset("{user:42}:profile", "name", "Ada")
pipe.hset("{user:42}:stats", "books", 12)
pipe.execute() # same slot, so it succeeds
# this one fails with CROSSSLOT
client.mget("user:42", "user:43")| Setting | Effect | Recommendation |
|---|---|---|
socket_timeout | Fails fast on a slow node | Set it below your request timeout, or a timeout cascades |
max_connections | Pool per node | Size to application threads divided by nodes |
retry_on_error | Retries connection and timeout errors | Only for idempotent commands |
health_check_interval | Reconnects idle connections | Below the server's timeout, or use it to detect a dead node |
decode_responses | Returns str instead of bytes | Convenient; costs an encode on writes |
- Retrying a command that is not idempotent can double a counter increment or a queue push. Retry reads freely, and make writes idempotent before enabling a retry policy.
- Cluster-aware clients refresh the slot map on MOVED, but a full topology change can take a few seconds - expect a brief period of errors during a reshard.
- Pipelines in cluster mode are per node. A client library that hides this still needs all keys in a pipeline to share a slot.
- Test the failover path: kill a primary and confirm the application recovers without a restart.
FAQ
Do I need a cluster?
Only when one instance cannot hold the dataset or serve the write throughput you need. A single node with a replica and good monitoring handles a surprising amount of traffic, and cluster mode adds slot routing, multi-key restrictions and resharding operational work.
How do I migrate to a cluster without downtime?
Add a prefix and a hash tag scheme first, deploy clients that read and write both keys so the new format fills in, backfill the old data, verify, then cut over and delete the old keys. The migration is a schema change, so treat it like one.
Related
Replication, Sentinel and high availability Performance, pipelining and monitoring
Last refreshed 2026-09-18.