Redis has one flat namespace per logical database. There are no tables, no schemas, no folders. Every key is a string sitting next to every other key. That sounds chaotic, but a simple convention makes it tidy.
The colon convention
We use : to separate logical parts of the key. It’s not enforced by Redis — it’s just what everyone does, and tools like RedisInsight render it as a tree.
SET user:42:name "Alice"
SET user:42:email "alice@example.com"
HSET user:42:profile age 30 city "Mumbai"
SADD user:42:followers 17 23 88
INCR page:home:views
SET session:abc123 "userId=42" EX 1800
The pattern is roughly: <entity>:<id>:<attribute> or <feature>:<scope>:<id>.
Why this matters
We can scan a slice of the keyspace cheaply:
SCAN 0 MATCH "user:42:*" COUNT 100
In simple language: SCAN is like a non-blocking KEYS. It returns keys in chunks so we never freeze the server. Never use KEYS in production — it walks the entire keyspace under the single-threaded loop and stalls everything.
Keep keys short, but readable
Long keys waste memory. Short keys hurt debugging. The sweet spot is short tokens with full meaning:
- Good:
u:42:n(production-tight) oruser:42:name(readable) - Bad:
the_full_user_record_for_user_with_id_42_named_alice
For a million keys, going from 40 bytes to 12 bytes per key saves ~28 MB. Not free, but not a fortune either — readability usually wins until you’re at billions of keys.
Selecting a database
Redis has 16 logical databases by default (0 through 15), selectable with SELECT n. Don’t rely on this for multi-tenancy — it’s a legacy feature. Cluster mode only supports DB 0. Use key prefixes (tenant42:user:7) for real isolation.
Keyspace notifications
Redis can publish events when keys change — handy for cache invalidation, expiration hooks, and audit trails. They’re off by default because they add overhead.
__keyspace@0__:user:42
(your app)
Turn them on, then subscribe:
CONFIG SET notify-keyspace-events KEA
# K = keyspace events, E = keyevent events, A = all event classes
SUBSCRIBE __keyevent@0__:expired
Now any time a key expires, we get a message. Classic use case: a user puts an item in their cart with a 30-minute TTL, and when the key expires we publish a “cart abandoned” event.
A few gotchas
- Cluster mode hashes keys to slots by default. If you want two keys on the same slot (for atomic multi-key ops), wrap the shared part in
{}:{user:42}:nameand{user:42}:cartwill live together. - Avoid spaces and binary garbage in keys — they make
redis-clidebugging painful. - Pick the convention at day zero. Migrating millions of keys later is no fun.