Chapter 02 · Keys, Namespaces, Expiration, TTL, Scanning, and Data-Type Introspection

Key Naming Schemes, Namespaces, Hash Tags, Cardinality, and Operational Discoverability

Design Redis key names that remain discoverable and collision-resistant, understand namespaces and Cluster hash tags, bound cardinality, and verify keyspace state safely.

Beginner → Intermediate100–125 minutesNaming + discoverability labRedis Open Source 8.10.1Free/local-firstLast reviewed: September 6, 2026

Learning outcomes

AtlasMart has reached the point where several teams write to the same Redis server. The cart service writes cart:42, a test harness writes the same short name, a marketing job creates one key per page view, and an engineer proposes “database 3 for tenant C” as isolation. None of those choices is a Redis syntax error, but together they create collisions, unbounded cardinality, weak discoverability, and a false security model. Before AtlasMart adds richer data structures, it needs a key-addressing discipline that humans and tools can reason about.

01

Design stable key names whose parts communicate ownership, environment, entity, identifier, and purpose without pretending that naming itself enforces access control.

02

Distinguish an application namespace, a standalone Redis logical database, and a Redis Cluster hash slot; explain what a Cluster hash tag changes.

03

Measure key cardinality and inspect representative keys with EXISTS, TYPE, TTL/PTTL, SCAN, DBSIZE, and MEMORY USAGE.

04

Design discovery workflows that do not depend on a full blocking KEYS operation or on exact SCAN enumeration semantics.

05

Repair a namespace-collision and tenant-isolation mistake, then leave a bounded, auditable AtlasMart fixture for the rest of Chapter 02.

Exact lab baseline

All Chapter 02 labs reuse the disposable Chapter 01 environment: Redis Open Source 8.10.1 from Docker Official Image redis:8.10.1, container atlasmart-redis-ch01, standalone topology, host publication 127.0.0.1:6379, TLS disabled because traffic stays on loopback, default user disabled, named ACL users atlasmart-app and academy-admin, logical database 0, AOF with appendfsync everysec plus RDB snapshots, and a persistent /data Docker volume. No explicit maxmemory limit or eviction policy is introduced in this chapter. Search, JSON, vector, time-series, and probabilistic features are not required for these exercises. The braces used by a Redis Cluster hash tag are taught now for naming discipline, but the lab remains standalone; braces therefore have no routing effect in this chapter.

1. A Redis key is an address, so naming is part of the data model

A key is the binary-safe name by which Redis locates a value. The set of keys visible in one selected logical database is its keyspace. Redis does not interpret colons in a key name; atlasmart:prod:cart:431 is just one sequence of bytes. Humans and applications give those separators meaning. A namespace is therefore an application convention: a predictable prefix or naming grammar used to avoid collisions and make ownership discoverable.

For AtlasMart, a useful grammar is atlasmart:<environment>:<domain>:<identifier>:<purpose>. The grammar should encode stable facts that operators need when investigating a key. It should not embed secrets, personally identifying values that do not need to be exposed in key names, or volatile implementation details. Long names consume memory on every key; very short ambiguous names save bytes at the cost of operational confusion. The right choice is evidence-based and workload-specific, not “always use exactly N characters.”

Candidate Interpretation Judgment
cart:431 Short cart identifier Collision-prone when several applications share a server; ownership/environment are invisible.
atlasmart:prod:cart:431 Product + environment + domain + id Good baseline for a standalone key when those components are stable.
atlasmart:prod:cart:{{431}}:items Adds a Cluster hash tag around 431 Useful only when future multi-key operations really need keys sharing this tag to share a hash slot.
atlasmart:prod:request:<uuid> for every request forever Per-request unique key Operationally dangerous unless lifetime/cardinality is explicitly bounded.
redis-cli · observe names as data, not hierarchy
docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 \  redis-cli --user atlasmart-app SET atlasmart:prod:cart:431 "open"docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 \  redis-cli --user atlasmart-app EXISTS atlasmart:prod:cart:431docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 \  redis-cli --user atlasmart-app TYPE atlasmart:prod:cart:431# Expected shape:# OK# (integer) 1# string

EXISTS proves that a key is currently addressable; TYPE reports the Redis data type stored at that key. Neither command proves who owns the key, whether its value is fresh, or whether the prefix is secure. Those are separate contracts.

2. Namespace conventions are not security boundaries

Redis Access Control Lists (ACLs) authenticate users and authorize commands and key/channel patterns. A naming prefix can make an ACL rule practical—for example, the Chapter 01 application user is restricted to ~atlasmart:*—but the prefix itself does not deny anything. If two tenants both authenticate as the same user with permission for ~atlasmart:*, writing atlasmart:tenant-a:... and atlasmart:tenant-b:... does not create tenant isolation. Application authorization, separate ACL users/key patterns, network boundaries, or separate Redis deployments may be required depending on the threat model.

A logical database is a numbered keyspace selected with SELECT in standalone Redis. It is convenient for some development separation, but it is not an ACL boundary: authentication and server resources are shared. Redis Cluster deliberately does not provide multiple logical databases in the same way; Cluster distributes keys through hash slots and uses database 0. Therefore “put each tenant in DB 1, 2, 3” is neither portable to Cluster nor a complete security architecture.

Security boundary test

Ask what mechanism would reject an unauthorized command. A colon prefix cannot reject it. An ACL rule, application authorization check, network policy, or separate deployment can. Naming supports those controls; it does not replace them.

3. Hash tags affect Cluster placement, not standalone meaning

Redis Cluster is Redis’s built-in sharding topology: keys are mapped into 16,384 hash slots, and those slots are assigned to cluster nodes. A hash tag is the non-empty substring between the first valid pair of braces in a key. When present, Cluster hashes that substring rather than the whole key, allowing deliberately related keys to map to the same slot. This matters because many multi-key commands and server-side operations in Cluster require all participating keys to be in one slot.

Hash tags should encode the smallest stable grouping that truly needs slot locality. Tagging every cart key with {cart} would force all carts into one slot and create skew; tagging related keys with {431} can preserve locality for cart 431 without collapsing every cart together. In the Chapter 02 standalone lab, {431} is just part of the key name. We teach it now so names do not need a disruptive rewrite later.

Key Cluster tag used Design consequence
atlasmart:cart:{{431}}:items 431 Related cart-431 keys can share a slot.
atlasmart:cart:{{431}}:totals 431 Same slot as the items key.
atlasmart:cart:{{}}:items No non-empty tag The normal whole-key hash is used.
atlasmart:{{cart}}:431 cart All similarly tagged carts target one slot; likely poor distribution.

Do not use braces merely because they look tidy. Slot locality is a topology decision with load-distribution consequences. Chapter 21 will create a real Cluster and verify slot movement, MOVED/ASK redirections, and hash-tag behavior.

4. Cardinality is a capacity property, not just a count

Cardinality here means the number of distinct keys. Every key has metadata overhead in addition to its value, and more keys increase the work needed for discovery, expiry bookkeeping, persistence, replication, backup, and Cluster operations. A million tiny values are not equivalent to one large value merely because their payload bytes sum to the same number.

DBSIZE returns the current number of keys in the selected logical database. It is useful as an inventory signal, not a breakdown by namespace. SCAN can incrementally visit keys and MATCH can filter names, but its cursor semantics are intentionally not snapshot semantics. A production cardinality policy should therefore combine application-level metrics (keys created, retained, expired, deleted), Redis database counts, memory metrics, and representative sampling rather than periodically blocking the server to recount everything.

redis-cli · cardinality and representative memory evidence
docker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch01 \  redis-cli --user academy-admin DBSIZEdocker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch01 \  redis-cli --user academy-admin MEMORY USAGE atlasmart:prod:cart:431docker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch01 \  redis-cli --user academy-admin SCAN 0 MATCH 'atlasmart:prod:cart:*' COUNT 20# DBSIZE is an exact current count for DB 0 at command execution.# MEMORY USAGE is an estimate of bytes attributable to this key/value according to this Redis build.# SCAN returns: 1) a new cursor, 2) zero or more matching keys.

The exact byte count from MEMORY USAGE depends on Redis version, allocator behavior, internal encoding, and sampling for nested types. It is evidence for this server, not a universal per-key constant. Redis 8.10 also changed some accounting for compact hash representations, another reason to record the server version beside measurements.

5. Operational discoverability means safe questions have cheap answers

A discoverable key design lets an operator answer “does it exist?”, “what type is it?”, “does it expire?”, “roughly how large is it?”, and “what family does it belong to?” without reading the whole dataset. TTL reports remaining lifetime in seconds, PTTL in milliseconds; both return negative sentinel values for persistent or missing keys. SCAN is an incremental iterator; a cursor of 0 starts a full iteration and a returned cursor of 0 ends it.

redis-cli · build a small discoverable family
docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 \  redis-cli --user atlasmart-app SET atlasmart:prod:session:1001 "customer=431" EX 120docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 \  redis-cli --user atlasmart-app SET atlasmart:prod:session:1002 "customer=772" EX 300docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 \  redis-cli --user atlasmart-app PTTL atlasmart:prod:session:1001docker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch01 \  redis-cli --user academy-admin SCAN 0 MATCH 'atlasmart:prod:session:*' COUNT 10# PTTL should be positive and decrease over time.# The SCAN cursor may already return 0 for this tiny fixture; that does not generalize to a large keyspace.

Because these session keys were created with EX 120 and EX 300, they are volatile keys: keys with expiration metadata. If PTTL reports -1, the key exists but is persistent; if it reports -2, the key is missing. Lesson 2 proves these lifecycle states and mutation rules in detail.

6. Deliberately wrong design: prefixes and DB numbers as isolation

Suppose two AtlasMart components both choose cart:431, while the platform team tells one tenant to use logical database 1 and assumes that this is “secure enough.” The collision is concrete in DB 0: whichever writer runs last replaces the string value. The isolation claim is also false: any authenticated identity allowed to use SELECT and access those keys can cross the database-number convention, and a future Redis Cluster migration removes the multi-database assumption.

redis-cli · controlled collision, diagnosis, and repair
# WRONG: two owners choose the same ambiguous key.docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 \  redis-cli --user atlasmart-app SET atlasmart:ch02:collision "cart-service"docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 \  redis-cli --user atlasmart-app SET atlasmart:ch02:collision "test-harness"docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 \  redis-cli --user atlasmart-app GET atlasmart:ch02:collision# "test-harness" -- the first owner's state was overwritten.# REPAIR: encode stable ownership/purpose in distinct keys.docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 \  redis-cli --user atlasmart-app MSET \  atlasmart:prod:cart:431:state "open" \  atlasmart:test:fixture:cart:431:state "synthetic"docker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch01 \  redis-cli --user academy-admin SCAN 0 MATCH 'atlasmart:*:cart:431:*' COUNT 20

The repaired names make the collision less likely and discovery easier. They still do not create authorization. If production requires tenant isolation, pair stable prefixes with independently reviewed ACL/application/network/deployment controls and test an unauthorized access attempt.

7. Hands-on lab: build the Chapter 02 key inventory

This lab creates a bounded synthetic dataset under atlasmart:ch02:. It does not call FLUSHDB or FLUSHALL. Cleanup targets only known chapter prefixes. The objective is to produce an evidence card, not a benchmark.

shell · create deterministic keys and inspect the keyspace
# Create four key families with bounded cardinality.docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 sh -lc 'for i in $(seq 1 12); do   redis-cli --user atlasmart-app SET "atlasmart:ch02:product:$i:summary" "p-$i" >/dev/null; done; for i in $(seq 1 6); do   redis-cli --user atlasmart-app SET "atlasmart:ch02:session:$i" "s-$i" EX 600 >/dev/null; done'# Collect evidence.docker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch01 \  redis-cli --user academy-admin DBSIZEdocker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch01 \  redis-cli --user academy-admin EXISTS atlasmart:ch02:product:1:summarydocker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch01 \  redis-cli --user academy-admin TYPE atlasmart:ch02:product:1:summarydocker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch01 \  redis-cli --user academy-admin TTL atlasmart:ch02:session:1docker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch01 \  redis-cli --user academy-admin MEMORY USAGE atlasmart:ch02:product:1:summarydocker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch01 \  redis-cli --user academy-admin SCAN 0 MATCH 'atlasmart:ch02:*' COUNT 100

Expected evidence is shape-based rather than byte-for-byte: EXISTS returns 1, TYPE returns string, the session TTL is positive and at most 600 when observed, memory usage is a positive integer, and a full SCAN iteration eventually returns the fixture keys. If the first SCAN returns a nonzero cursor, continue with exactly that cursor until Redis returns 0.

Verification checklist:

  • You can explain every component of the AtlasMart naming grammar and which components are conventions rather than Redis-enforced hierarchy.
  • You can state why logical database numbers are not tenant/security boundaries and why Redis Cluster changes the database model.
  • You can explain how a hash tag influences a future Cluster slot without claiming that braces do anything in this standalone lab.
  • You recorded current DBSIZE, TYPE, TTL/PTTL, MEMORY USAGE, and SCAN evidence without using KEYS for inventory.
  • You can identify at least one cardinality budget for each new key family: creation rate × retention window plus exceptional backlog.
shell · cleanup only this lesson fixture
docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 sh -lc 'for i in $(seq 1 12); do redis-cli --user atlasmart-app UNLINK "atlasmart:ch02:product:$i:summary" >/dev/null; done; for i in $(seq 1 6); do redis-cli --user atlasmart-app UNLINK "atlasmart:ch02:session:$i" >/dev/null; done; redis-cli --user atlasmart-app UNLINK atlasmart:ch02:collision >/dev/null; redis-cli --user atlasmart-app UNLINK atlasmart:prod:cart:431 atlasmart:prod:cart:431:state atlasmart:test:fixture:cart:431:state atlasmart:prod:session:1001 atlasmart:prod:session:1002 >/dev/null' 

8. Production judgment

Key naming is appropriate as an operational and modeling convention, not as an authorization mechanism. Bound cardinality before launch: estimate creation rate, retention, fan-out per entity, and failure backlog; then measure actual key counts, memory, persistence growth, and tail latency. Remember that persistence writes key changes to disk according to the configured RDB/AOF policy, replication later copies mutations asynchronously to replicas, Sentinel later automates failover, and Cluster later partitions keys by slots. None of those mechanisms changes the basic need for stable names.

Search indexes, vector retrieval, time-series structures, and probabilistic structures can add secondary memory and write costs later, but they do not make the primary keyspace disappear. Client retries can also create duplicate or abandoned keys if key generation is not idempotent. Test namespace collisions, expired/missing keys, bounded bulk creation, scan under mutation, and cleanup during failure. Keep migration and rollback in mind: renaming millions of keys or changing hash tags can be operationally expensive and, in Cluster, can alter slot placement.

9. Summary and next step

A Redis key name is a binary-safe address. Colons and prefixes are application conventions; ACLs and other controls enforce security. Logical databases are separate keyspaces in standalone Redis but not a tenant-isolation mechanism, while Redis Cluster uses 16,384 hash slots and optional hash tags for placement. Cardinality is a capacity concern, and safe discoverability starts with bounded names plus incremental inspection. Next, you will make each key’s lifetime equally explicit by proving expiration semantics command by command.

Check your understanding

  1. Why is atlasmart:tenant-a: a useful prefix but not a security boundary?
  2. What does a Cluster hash tag change?
  3. Why is DBSIZE insufficient for namespace capacity planning?
  4. What does PTTL = -1 versus -2 mean?
  5. Why should a future Cluster migration influence naming today?
Review the answers

1. It improves naming and can be referenced by ACL rules, but the bytes in the name do not reject unauthorized commands. Enforcement comes from ACLs, application authorization, network/deployment controls, or other security mechanisms.

2. In Redis Cluster, the first valid non-empty brace substring is hashed for slot selection so intentionally related keys can share a slot. In a standalone server it has no routing effect.

3. It is an exact current count for the selected database, not a per-prefix time series and not a forecast. You also need creation rate, retention, memory, skew, expiration/deletion, and failure backlog.

4. -1 means the key exists with no expiration; -2 means the key does not exist at observation time.

5. Hash tags and the lack of multiple logical databases in Cluster can make key-name and DB-number assumptions costly to change later, so topology-aware naming reduces migration risk.

Authoritative references

Keep knowledge open

Help the academy stay free and grow.

If these tutorials save you time, a small donation supports new lessons, technical review, diagrams, examples, and long-term maintenance.

ETHEthereum / ERC-20 only
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0

Send only Ethereum or ERC-20 compatible assets to this address.