Command Palette

Search for a command to run...

Hectal
PHASE 0Beginner ~16 min· topic 5 of 5

Topic 0.5

Key Design: Names, Namespaces, Tenants, and Versions

In one line

Keys are Redis's only index, so their names are your schema. Good keys are predictable, namespaced, tenant-aware and versioned, and they're designed with cluster slots, memory and migrations in mind.

0/5 · 0%

Think of it like this

A warehouse with a strict shelf-labelling system, like aisle 12, bay 4, shelf C. Anyone can find anything, two products never share a label, and when the layout changes you relabel in a planned way. A warehouse where people write "stuff" on boxes works until it doesn't.

Key ideas

  1. 01

    Use a consistent, colon-separated hierarchy: <service>:<entity>:<id>[:<field>], for example cart:user:123, product:456:views, session:9f3c..., rl:api:v1:user:123:1727520000. The colon is only a convention (Redis treats the key as bytes), but tools, dashboards and SCAN patterns rely on it.

  2. 02

    Prefix by owner to prevent collisions between services and to scope permissions: an ACL can allow ~checkout:* for one service only. In multi-tenant systems, put the tenant early (t:acme:user:123) so you can scan, count and delete one tenant's data, and so a noisy tenant is visible in key analysis.

  3. 03

    Version keys that hold serialised objects: product:v3:456. When a deployment changes the format, new code reads and writes v3 while old pods still use v2; old keys expire naturally. This avoids crashes from deserialising an old format and makes rollbacks safe. The same trick invalidates a whole cache namespace at once (bump v3 to v4).

  4. 04

    Plan for Redis Cluster from day one: multi-key commands (MGET, transactions, Lua over several keys) only work when all keys hash to the same slot. Hash tags ({user:123}:cart, {user:123}:profile) force that, since only the part inside {} is hashed (Topic 11.2). Choose tags that group data you use together, without making one tag enormous.

  5. 05

    Keep keys short but readable: a key is stored once per entry, so 100M keys × 40 extra bytes = 4 GB. Don't encode large values in keys, and don't put unbounded user input in key names without validating it (it's a memory and collision risk).

  6. 06

    Put a TTL on every key that doesn't need to live forever, and decide it at design time. Most memory leaks in Redis are "keys without TTL that nobody deletes".

Code & diagrams

key-conventions.redisredis
# service : entity : id : facet
SET   catalog:product:v3:456 "{...json...}" EX 3600
HSET  session:9f3c2b profile:user 123 created 1727520000
EXPIRE session:9f3c2b 1800
INCR  rl:api:user:123:1727520000       # fixed-window rate limit bucket
SADD  t:acme:user:123:roles admin       # tenant-scoped

# Cluster-ready: same hash tag, same slot
HSET  {user:123}:profile name "Asha"
HSET  {user:123}:cart sku:9 2
CLUSTER KEYSLOT {user:123}:profile      # (integer) 5236
CLUSTER KEYSLOT {user:123}:cart         # (integer) 5236
keys.javajava

Centralise key construction; never build keys with ad-hoc string concatenation across the codebase.

public final class Keys {
    private static final String PRODUCT_VERSION = "v3";

    public static String product(long id) {
        return "catalog:product:" + PRODUCT_VERSION + ":" + id;
    }

    public static String cart(long userId) {
        return "{user:" + userId + "}:cart";     // colocated with profile
    }

    public static String rateLimit(long userId, long windowStartSec) {
        return "rl:api:user:" + userId + ":" + windowStartSec;
    }
}
migrate-key-format.shbash

A safe background migration: iterate with SCAN, copy with the new name, let old keys expire.

redis-cli --scan --pattern 'product:*' --count 1000 | while read -r key; do
  id="${key#product:}"
  # COPY keeps the value and TTL (Redis 6.2+); NX-like behaviour: fails if target exists
  redis-cli COPY "$key" "catalog:product:v3:$id" > /dev/null
done
# Readers try the new key first and fall back to the old one during the migration window.

Interview problem

The problem

Key design for a multi-tenant SaaS

Design the Redis key scheme for a multi-tenant SaaS with sessions, per-tenant feature flags, per-user rate limits, and cached dashboards. It will run on Redis Cluster. Support deleting all data for a tenant, and changing the dashboard cache format without downtime.

You're given

  • 5,000 tenants, some very large
  • Redis Cluster, 6 shards
  • GDPR tenant deletion
  • Weekly deployments

The interviewer follows up

01

Why not use Redis logical databases (SELECT 0–15) to separate tenants?

02

Your SCAN for tenant deletion finds nothing on some keys. Why?

When it breaks

A deployment changes the serialised format without changing the key

What you see

During the rolling deploy, old pods read new-format values (or the reverse) and throw deserialisation errors; some requests fail until all pods are updated or the cache is flushed.

Fix & prevent

Version the key namespace (v3 → v4) whenever the format changes; keep formats backward-compatible when you can (Topic 8.3).

Keys built from raw user input

What you see

An attacker sends millions of unique IDs, creating millions of keys (memory growth) or colliding with other namespaces via crafted separators.

Fix & prevent

Validate and normalise IDs before building keys, cap key counts per user, always set TTLs, and hash long or untrusted parts.

Explain it without notes

01

Why is key versioning useful during rolling deployments?

02

How does a hash tag change which cluster slot a key belongs to, and what's the risk?

Practice

01

Write key names for: a user's unread notification count, a product's view counter for one day, and a cached search result for a query string.

02

Run CLUSTER KEYSLOT (it works on a standalone server) for {order:9}:items and {order:9}:status. Why do they match?

Trade-offs

  • ↔

    Readable long keys vs memory: at hundreds of millions of keys, shorter prefixes save gigabytes; at smaller scales readability wins.

  • ↔

    Hash tags enable multi-key atomicity in Cluster but concentrate load; tag by the smallest unit you need atomicity for (user, order), never by tenant or "global".

Done when you can

  • I can design a namespaced, tenant-aware, versioned key scheme.

  • I can migrate a key format without downtime.

  • I can explain hash tags and choose tags that won't create hot shards.

  • Every key I design has a TTL decision attached.