Keys & TTL
Redis keys are flat strings, but the conventions you choose determine ergonomics, TTLs, sharding, and scanning.
Redis — key design
EXAMPLE
# ===== Keys are bytes (typically UTF-8 strings) =====
# Length limit: 512MB (practically: keep them short).
# The data type lives at the key. SET user:1 'alex' creates a String type at 'user:1'.
# ===== Naming convention: <domain>:<entity>:<id>[:<attr>] =====
# Good:
# user:1 # full user hash
# user:1:friends # set of friend ids for user 1
# session:abc123 # session token
# rl:login:198.51.100.7 # rate-limit counter
#
# Bad:
# u_1, user/1, user|1 -- inconsistent
# user_first_name_alex -- value-in-key; impossible to mutate cleanly
# user:1:profile:settings:theme -- too deep; pick the right type instead
# Use colons as separators (Redis tooling recognises them).
# ===== TTLs are first-class =====
SET session:abc123 "user_42" EX 1800 # expire in 30 minutes
EXPIRE session:abc123 3600 # extend TTL
PERSIST session:abc123 # remove TTL
TTL session:abc123 # seconds remaining (-1 = no TTL, -2 = missing)
# Pattern: always set a TTL on caches and sessions. Long-lived keys belong in a real DB.
# ===== Multi-set / multi-get =====
MSET a 1 b 2 c 3
MGET a b c
DEL b c
# ===== Existence and counts =====
EXISTS user:1 user:2 # count of keys that exist
DBSIZE # number of keys in current DB
# ===== Scanning safely (don't use KEYS in prod) =====
# KEYS is O(N), blocks the server.
# SCAN is cursor-based, non-blocking.
SCAN 0 MATCH 'session:*' COUNT 200
# Repeat until cursor returns 0.
# In code (node-redis):
# const stream = client.scanIterator({ MATCH: 'session:*', COUNT: 200 });
# for await (const key of stream) { /* ... */ }
# Type-specific scans:
HSCAN user:1 0 MATCH 'pref:*' COUNT 50
SSCAN user:1:friends 0 COUNT 50
ZSCAN leaderboard 0 COUNT 50
# ===== Atomic counters =====
INCR rl:login:198.51.100.7
EXPIRE rl:login:198.51.100.7 60 # set TTL on first hit
# Idempotent pattern: INCR + EXPIRE NX in a pipeline / transaction.
# ===== Hash tags for cluster routing =====
# In Redis Cluster, keys hash to slots. Hash tag forces keys to the same slot.
# Bracket-wrapped substring = the only part used for hashing.
SET {user:1}:profile "..."
SET {user:1}:settings "..."
# Both live on the same node. Necessary for multi-key ops (MGET, transactions, scripts).
# ===== Renaming + copying =====
RENAME session:old session:new
COPY user:1 user:1:backup TTL EX 86400
# ===== Patterns to internalise =====
# - <domain>:<entity>:<id>[:<attr>] naming, colons as separators
# - TTL on everything cache-shaped; KEEPTTL on rewrites you don't want to extend
# - SCAN, never KEYS, in any environment with real traffic
# - Hash tags when multi-key ops are required in a cluster
# - One Redis DB per environment (number); avoid DB indexes for multi-tenant separation
# ===== Pitfalls =====
# - KEYS * in prod -> server stalls; alarms fire
# - Putting mutable values inside the key name -> cannot update without delete+set
# - Forgetting TTL on a 'temporary' cache key -> grows forever
# - 4MB+ keys (e.g. caching pages) -> memory pressure, slow eviction
# - Sharing one Redis between unrelated apps without prefixes -> noisy collisions
Why it matters
Key naming is the API of your Redis. Pick a colon-separated convention, set TTLs by default, scan instead of keys, and use hash tags when the cluster forces multi-key locality. Once these are reflex, you can read another teams Redis without asking what anything means.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
SET name "Ada" GET name EXISTS name # 1 TTL name # -1 (no expiry) EXPIRE name 60 # expires in 60s DEL nameTry it Yourself »
Exercise
Set a TTL of 60 seconds on a key.
session 60
Six letters.
Discussion
Loading…