Foundations 2 of 5 · Visual guide

Key names, patterns, and expiry

Redis has no tables or folders. The key name is your schema, and a TTL is how a key cleans itself up.

Name keys like paths

Anatomy of a key name The key shop:user:42:cart has four segments separated by colons: namespace, object type, id, and field. shop : user : 42 : cart app / service object type id field / sub-object Read it left to right like a URL. SCAN … MATCH shop:user:* finds every user.
Colons are a convention, not a feature — but every Redis GUI groups keys by them.
Do

user:42:profile, session:9f2c, rl:ip:1.2.3.4
Version caches: cache:v2:product:42 — bump v2 to drop every old entry at once.
Keep names short but readable; they live in RAM too.

Don't

u42 (nobody knows what it is), a full URL or JSON blob as the key, spaces and mixed case, user input pasted straight in without a prefix.

Find keys: KEYS vs SCAN

KEYS blocks, SCAN interleaves KEYS walks all ten million keys in one command, about two seconds, while every other client waits. SCAN walks small slices and lets other clients' commands run in between. KEYS user:* other clients SCAN cursor other clients walks all 10,000,000 keys in one go · ~2 s GET · SET · INCR · logins … all waiting run 0→1840 →9012 GET SET → … INCR → 0 ✓ GET time →
  1. KEYS user:* checks every key in one command. With 10 million keys that takes seconds.
  2. Redis runs one command at a time, so every other client waits behind it: logins, cache reads, everything.
  3. SCAN 0 MATCH user:* COUNT 100 checks a small slice and returns the next cursor (1840).
  4. Between slices, other clients' commands run. Nobody waits more than a moment.
  5. Call SCAN again with each cursor until it returns 0. Same keys, no outage. A key can appear twice, so de-duplicate.
Use KEYS only on a laptop. In production, SCAN (or HSCAN, SSCAN, ZSCAN inside one big key).

Give a key a TTL

TTL → 37seconds left before it disappears
TTL → -1key exists, never expires
TTL → -2key does not exist (or already expired)
Expiry commands
SET k v EX 60Write and expire in one atomic command. Prefer this over SET then EXPIRE.
EXPIRE k 60 [NX|XX|GT|LT]Set a TTL on an existing key. NX: only if it has none (rate limiters).
EXPIREAT k 1767225600Expire at a Unix time: “end of the sale”.
TTL / PTTLSeconds / milliseconds left.
PERSIST kRemove the TTL. A plain SET also removes it unless you pass KEEPTTL.

How expiry really works

Lazy and active expiry Fifteen keys have TTLs; six have passed their time. Lazy expiry deletes a key when a client touches it. Active expiry samples twenty keys ten times a second, deletes the expired ones, and samples again if more than a quarter were expired. keys with a TTL k1 k2 ⌛ k3 k4 k5 ⌛ k6 k7 ⌛ k8 k9 ⌛ k10 k11 k12 ⌛ k13 k14 ⌛ k15 sample: k1–k5, k11, k12 → 3 of 7 expired (43%) 43% > 25% → sample again → k9, k14 deleted all expired keys gone · memory reclaimed Lazy · on access GET k7 → expired? → DEL reply (nil) Active · in the background 10× per second: sample 20 keys delete the expired ones > 25% expired? sample again
  1. Redis keeps a second table: key → expiry time. Only keys with a TTL live there. Six of these have passed their time (⌛) but are still in memory.
  2. Lazy expiry: a client runs GET k7. Redis checks the time, sees it passed, deletes k7, and replies (nil). An expired key is never returned.
  3. But keys nobody reads would sit in RAM forever. So a background job runs 10 times a second…
  4. …it picks 20 random keys that have a TTL (here 7, to fit the picture) and deletes the expired ones: k2, k5, k12.
  5. 3 of 7 were expired — more than 25%. That means many more are probably waiting, so it samples again right away and finds k9 and k14.
  6. Result: reads are always correct, and memory is reclaimed in small, cheap steps instead of one big scan.
Two mechanisms: lazy (on touch) guarantees correctness, active (random sampling) frees memory.

Watch active expiry

Lab · 60 keys, a sampler, and the 25% rule

Press “Time passes”, then run cycles. Watch how a high expired ratio triggers extra samples.

Expiry is not eviction

Expiry · your decision

You set a TTL. When it passes, the key is gone, even if memory is empty.

Eviction · Redis's decision

Memory hits maxmemory. The maxmemory-policy picks victims: allkeys-lru, allkeys-lfu, volatile-lru (only keys with a TTL), volatile-ttl, or noeviction (writes fail instead).

Quick check

You SET session:abc … again to update it. What happened to its 30-minute TTL?
Show answer

It is gone — a plain SET clears the TTL, and the session now lives forever. Use SET … EX 1800 or KEEPTTL, or update a hash field with HSET, which keeps the TTL.

A key expired 2 seconds ago but INFO memory still counts it. Is that a bug?
Show answer

No. Nobody touched it yet (lazy) and the sampler has not picked it yet (active). Reads already return (nil); memory follows shortly.