Topic 11.2
Hash Tags and Multi-Key Operations
In one line
If a key contains {...} with at least one character inside, only that part is hashed. Keys that share a tag land in the same slot, which makes MGET, transactions and Lua across them possible. Choose tags carefully: too coarse a tag creates hot and oversized shards.
Think of it like this
A family booking hotel rooms under one surname. Every room booked under "Sharma" is placed on the same floor, so the family can move between rooms easily. But if a huge wedding party books under one surname, that floor overflows while others sit empty.
Key ideas
- 01
Rules: Redis hashes the substring between the first
{and the first}after it, if that substring is non-empty.{user:123}:cartand{user:123}:profileshare a slot;user:{}:xhas an empty tag, so the whole key is hashed; only the first tag counts. - 02
Without a shared slot, multi-key commands fail with
CROSSSLOT Keys in request don't hash to the same slot. That includesMGET,MSET,SINTER,RENAME,SMOVE,BLMOVEacross keys,MULTItouching several keys, and Lua or Functions with severalKEYS. - 03
Pick the tag at the level where you need atomicity: a user, an order, a conversation. Never a tenant, a date, or a constant like
{global}, since everything with that tag becomes one shard's problem. - 04
Some operations don't need tags: pipelines across slots (clients split them per node) and client-side joins (fetch separately, combine in the app). Use tags only where atomicity or a single round trip truly requires it.
- 05
Check the distribution: sample keys, compute slots, and verify load and memory spread (
redis-cli --cluster infoshows keys per node; watch per-node ops).
Code & diagrams
127.0.0.1:6379> CLUSTER KEYSLOT user:123:profile
(integer) 12291
127.0.0.1:6379> CLUSTER KEYSLOT user:123:cart
(integer) 9189
127.0.0.1:6379> MGET user:123:profile user:123:cart
(error) CROSSSLOT Keys in request don't hash to the same slot
127.0.0.1:6379> CLUSTER KEYSLOT {user:123}:profile
(integer) 5236
127.0.0.1:6379> CLUSTER KEYSLOT {user:123}:cart
(integer) 5236
127.0.0.1:6379> MGET {user:123}:profile {user:123}:cart
1) "{...}"
2) "{...}"Both keys share the {user:123} tag, so this script is valid in Cluster.
-- KEYS[1] = {user:123}:cart KEYS[2] = {user:123}:profile
local tier = redis.call('HGET', KEYS[2], 'tier')
local items = redis.call('HGETALL', KEYS[1])
if #items == 0 then return redis.error_reply('EMPTY_CART') end
redis.call('HSET', KEYS[2], 'lastCheckout', ARGV[1])
return {tier, items}Interview problem
The problem
Atomic profile + cart update in a cluster
You need to read the user's profile and cart and update both in one atomic operation on Redis Cluster. Design the keys. Then: what happens if one user becomes extremely hot?
The interviewer follows up
Can you move one hot hash tag to its own node?
When it breaks
Lua scripts developed on standalone Redis with untagged keys
What you see
In Cluster they fail with CROSSSLOT, or with undeclared keys, fail unpredictably depending on which node runs them.
Fix & prevent
Run CI against a real cluster (for example a 3-node cluster in Docker) and enforce tags for multi-key operations.
Explain it without notes
Explain how hash tags work and the rule for choosing a tag.
Practice
Rewrite three of your multi-key operations to be cluster-safe, either with tags or by splitting them.
Trade-offs
- ↔
Tags enable atomic multi-key logic at the cost of distribution; use them narrowly.
Done when you can
I can design hash tags for atomic operations without creating hot shards.
I recognise every kind of CROSSSLOT failure.