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

EXPIRE/PEXPIRE, EXPIREAT, TTL/PTTL, PERSIST, Conditional Expiration, and Lifecycle Semantics

Prove Redis expiration semantics command by command: relative and absolute deadlines, TTL/PTTL states, conditional expiry, PERSIST, mutation effects, and the limits of expiration timing.

Intermediate110–140 minutesExpiration semantics labRedis Open Source 8.10.1Free/local-firstLast reviewed: September 6, 2026

Learning outcomes

AtlasMart uses Redis for sessions, carts, one-time verification state, rate-limit windows, and cached catalog fragments. A developer says “set TTL to 30 minutes and Redis deletes it exactly 30 minutes later,” another uses SET to refresh a session value and accidentally removes its deadline, and a privacy runbook treats key expiration as proof that every persisted copy has disappeared. The commands look simple; the lifecycle contract is not. This lesson makes the deadline metadata and its mutation rules observable.

01

Choose relative second/millisecond and absolute timestamp expiry commands from the application’s time model.

02

Interpret TTL/PTTL sentinel values and use PERSIST deliberately.

03

Use NX, XX, GT, and LT conditional expiration without confusing their comparison semantics.

04

Prove which common mutations preserve an existing timeout and which replacement operations clear it.

05

Explain why Redis expiration is a data-lifecycle mechanism rather than an exact task scheduler or complete privacy-erasure proof.

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. All timing examples use the Redis server clock for enforcement. Host/container scheduling can make observed values differ by small amounts, so expected output is expressed as ranges or sentinel states rather than an exact millisecond.

1. Expiration attaches a deadline to a key

A key with an expiration is called volatile; a key without one is persistent until another command deletes, overwrites, evicts, or otherwise removes it. Time to live (TTL) is the remaining duration before the configured deadline. Redis stores expiration metadata separately from the value and checks it through passive access and active expiration work. The deadline determines when the key is logically expired, but Redis does not promise to run application code or reclaim every byte at the exact deadline tick.

This distinction matters for AtlasMart. A session key can become unavailable after its deadline even if physical memory reclamation or a notification arrives slightly later. If the business requirement is “send a message at exactly 10:00:00,” use a scheduler or queue designed for delivery semantics; do not turn a TTL into a clock-trigger guarantee.

redis-cli · see the three core TTL states
docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 \  redis-cli --user atlasmart-app SET atlasmart:ch02:ttl:persistent "A"docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 \  redis-cli --user atlasmart-app TTL atlasmart:ch02:ttl:persistent# -1  -> key exists, no expirationdocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 \  redis-cli --user atlasmart-app TTL atlasmart:ch02:ttl:missing# -2  -> key does not exist

2. EXPIRE, PEXPIRE, and EXPIREAT express different clocks

EXPIRE key seconds sets a relative deadline in whole seconds. PEXPIRE key milliseconds uses relative milliseconds. EXPIREAT key unix-time-seconds sets an absolute deadline expressed as a Unix timestamp in seconds; PEXPIREAT is the millisecond absolute counterpart. Relative expiry is convenient when the policy is “N seconds from now.” Absolute expiry is appropriate when many keys must align to the same business deadline—but it couples correctness to wall-clock time and still does not create exact scheduled execution.

Command Time expression Typical use Important boundary
EXPIRE k 60 60 seconds from command execution session/cache lifetime Resolution is seconds; expiration is not a callback scheduler.
PEXPIRE k 1500 1500 milliseconds from now short leases/windows Higher deadline precision does not guarantee exact reclamation timing.
EXPIREAT k 4102444800 Unix second 4102444800 shared calendar deadline Past timestamps delete immediately; depends on wall-clock correctness.
PEXPIREAT Unix milliseconds millisecond absolute deadline Same scheduling caveat; use only when the application truly needs absolute time.
redis-cli · relative and absolute expiry
docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 \  redis-cli --user atlasmart-app SET atlasmart:ch02:ttl:relative "session"docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 \  redis-cli --user atlasmart-app EXPIRE atlasmart:ch02:ttl:relative 90# (integer) 1docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 \  redis-cli --user atlasmart-app PTTL atlasmart:ch02:ttl:relative# Positive value <= 90000, decreasing with time.docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 \  redis-cli --user atlasmart-app SET atlasmart:ch02:ttl:absolute "campaign"docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 \  redis-cli --user atlasmart-app EXPIREAT atlasmart:ch02:ttl:absolute 4102444800# 4102444800 is 2100-01-01T00:00:00Z; chosen only as a deterministic future lab timestamp.

EXPIRE returns 1 when it sets the deadline and 0 when it cannot—for example because the key is missing or a conditional option rejects the change. Observe the reply; do not assume that issuing a command means the lifecycle changed.

3. TTL/PTTL observe lifetime; PERSIST removes it

TTL returns remaining whole seconds; PTTL returns remaining milliseconds. Their -1 and -2 sentinel states are part of the API contract and must be handled explicitly by clients. PERSIST key removes an existing expiration and leaves the value in place. It returns 1 when a timeout was removed and 0 when no such change was possible.

redis-cli · observe and remove the deadline
docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 \  redis-cli --user atlasmart-app SET atlasmart:ch02:ttl:cart "open" EX 120docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 \  redis-cli --user atlasmart-app TTL atlasmart:ch02:ttl:cart# positive, normally 120 or slightly lowerdocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 \  redis-cli --user atlasmart-app PERSIST atlasmart:ch02:ttl:cart# (integer) 1docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 \  redis-cli --user atlasmart-app PTTL atlasmart:ch02:ttl:cart# (integer) -1

Removing a TTL can be a feature—for example an operator promotes temporary state to durable state—but it can also be a retention bug. Treat PERSIST permission as a lifecycle capability, not a harmless read operation.

4. Conditional expiration lets the server enforce one deadline transition atomically

Since Redis 7, the expiration family supports mutually exclusive conditions. NX applies the new deadline only when the key has no expiry. XX applies only when it already has one. GT applies only when the new expiry is later than the current expiry; LT applies only when it is earlier. For GT/LT comparison, a persistent key is treated as having an infinite TTL. These checks happen with the expiration command itself, avoiding a client-side “read TTL, then race another writer, then write” gap.

redis-cli · prove NX, XX, GT, and LT
docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 \  redis-cli --user atlasmart-app SET atlasmart:ch02:ttl:conditional "token"# Persistent key: NX can add the first expiry.docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 \  redis-cli --user atlasmart-app EXPIRE atlasmart:ch02:ttl:conditional 100 NX# 1# NX now fails because an expiry already exists.docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 \  redis-cli --user atlasmart-app EXPIRE atlasmart:ch02:ttl:conditional 200 NX# 0# GT extends only if later; LT shortens only if earlier.docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 \  redis-cli --user atlasmart-app EXPIRE atlasmart:ch02:ttl:conditional 200 GT# 1docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 \  redis-cli --user atlasmart-app EXPIRE atlasmart:ch02:ttl:conditional 30 LT# 1

Because time elapses between commands, do not compare the final TTL to exact integers in a test. Assert ranges and state transitions: expiry absent/present, greater/less than a threshold, or missing after the deadline.

5. Mutations can preserve or clear expiration metadata

The key lifecycle is affected by what a command does to the key. In-place mutations such as INCR, LPUSH, and HSET generally leave the existing timeout attached. Replacing the value with SET clears the previous timeout unless the KEEPTTL option is used. RENAME transfers the source key’s timeout to the new name. These rules are why a session “refresh” implemented as a plain SET can accidentally turn expiring state into persistent state.

redis-cli · prove timeout preservation and clearing
# In-place numeric mutation preserves TTL.docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 \  redis-cli --user atlasmart-app SET atlasmart:ch02:ttl:counter 1 EX 120docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 \  redis-cli --user atlasmart-app INCR atlasmart:ch02:ttl:counterdocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 \  redis-cli --user atlasmart-app TTL atlasmart:ch02:ttl:counter# still positive# Replacement SET clears TTL unless KEEPTTL is requested.docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 \  redis-cli --user atlasmart-app SET atlasmart:ch02:ttl:counter 99docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 \  redis-cli --user atlasmart-app TTL atlasmart:ch02:ttl:counter# -1docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 \  redis-cli --user atlasmart-app EXPIRE atlasmart:ch02:ttl:counter 120docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 \  redis-cli --user atlasmart-app SET atlasmart:ch02:ttl:counter 100 KEEPTTLdocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 \  redis-cli --user atlasmart-app TTL atlasmart:ch02:ttl:counter# still positive

Never memorize a blanket statement such as “writes reset TTL” or “writes preserve TTL.” Read the command contract and test the exact mutation used by your client library and Redis version.

6. Expiration is observable over time, but it is not an exact scheduler

Redis expires keys through two complementary mechanisms. A passive check notices that a requested key is past its deadline and treats it as expired. Active expiration work also samples volatile keys so cold expired data does not remain indefinitely untouched. The application contract is about key availability after the deadline, not “an operating-system thread will free these exact bytes at millisecond T.”

shell · observe a short-lived key from the client side
docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 \  redis-cli --user atlasmart-app SET atlasmart:ch02:ttl:short "gone-soon" PX 1500docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 \  redis-cli --user atlasmart-app PTTL atlasmart:ch02:ttl:short# positive, <= 1500# Wait longer than the configured deadline on the host.sleep 2# PowerShell equivalent: Start-Sleep -Seconds 2docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 \  redis-cli --user atlasmart-app EXISTS atlasmart:ch02:ttl:short# 0

This proves that the key is no longer addressable after the wait. It does not measure the exact moment of physical reclamation, notification delivery, AOF rewrite removal, replica observation, or backup retention. Those are distinct mechanisms.

7. Deliberately wrong requirement: “TTL proves privacy erasure at exactly T”

An unsafe AtlasMart design stores a password-reset token with a 15-minute TTL and declares: “At 15:00.000 it is physically gone everywhere, therefore our retention obligation is satisfied.” Several assumptions are hidden inside that sentence. Redis expiration is not a callback scheduler; replicas receive expiry-related state through replication semantics; AOF/RDB persistence and off-host backups have their own retention; and application logs or secondary systems may contain copies.

The repair is to define the requirement precisely. For authentication correctness, reject a token after its Redis deadline and make token use one-time/idempotent. For privacy retention, inventory every copy, set lifecycle policy on primary storage and backups, test restoration behavior, and document the allowable deletion window. TTL remains valuable, but it is one enforcement mechanism inside a broader retention design.

Failure-injection boundary

The lab proves logical expiration of disposable keys. It does not manipulate the system clock, corrupt persistence files, or claim backup erasure. Clock-skew and recovery behavior belong in isolated topology/backup drills later in the course.

8. Hands-on lab: a TTL transition matrix

Create four keys that exercise persistent, relative, conditional, and absolute states. Record the server version with the results so a future learner can distinguish a behavior change from a memory error.

redis-cli · capture version and TTL state transitions
docker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch01 sh -lc 'redis-cli --user academy-admin INFO server | grep redis_version'# A: persistent -> volatile -> persistentdocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app SET atlasmart:ch02:matrix:a Adocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app TTL atlasmart:ch02:matrix:adocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app EXPIRE atlasmart:ch02:matrix:a 120 NXdocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app PERSIST atlasmart:ch02:matrix:a# B: millisecond deadlinedocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app SET atlasmart:ch02:matrix:b Bdocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app PEXPIRE atlasmart:ch02:matrix:b 90000docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app PTTL atlasmart:ch02:matrix:b# C: absolute timestampdocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app SET atlasmart:ch02:matrix:c Cdocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app EXPIREAT atlasmart:ch02:matrix:c 4102444800# D: prove replacement SET clears TTL, then repair with KEEPTTLdocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app SET atlasmart:ch02:matrix:d D EX 120docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app SET atlasmart:ch02:matrix:d D2docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app TTL atlasmart:ch02:matrix:d# -1

Verification checklist:

  • You observed -1 for an existing persistent key and -2 for a missing key.
  • You saw EXPIRE/PEXPIRE/EXPIREAT return 1 only when the expiry was applied, and you tested a conditional command that can return 0.
  • You verified that an in-place mutation can preserve TTL while a replacement SET clears it unless KEEPTTL is used.
  • You verified logical expiration with EXISTS after a safe wait, without claiming an exact physical deletion instant.
  • You can distinguish key expiry, eviction from maxmemory pressure, explicit deletion, persistence retention, and backup retention.
redis-cli · cleanup Chapter 02 TTL fixtures
docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app UNLINK \  atlasmart:ch02:ttl:persistent atlasmart:ch02:ttl:relative atlasmart:ch02:ttl:absolute \  atlasmart:ch02:ttl:cart atlasmart:ch02:ttl:conditional atlasmart:ch02:ttl:counter atlasmart:ch02:ttl:short \  atlasmart:ch02:matrix:a atlasmart:ch02:matrix:b atlasmart:ch02:matrix:c atlasmart:ch02:matrix:d

9. Production judgment

Use TTLs when state has a natural validity or retention window and rediscovering/recomputing it is acceptable. Do not use them as a universal durability, scheduling, privacy, or queue-delivery primitive. Expiration metadata consumes memory and expiration work can contribute to latency under large synchronized expiry waves, so avoid creating huge cohorts with identical deadlines without measuring behavior; application-level jitter can spread cache refresh load when semantics permit it.

Replication and Sentinel do not turn expiry into synchronous multi-node deletion; Cluster distributes volatile keys by slot; persistence determines restart state; maxmemory eviction is a separate capacity mechanism. Client retry logic must treat expiry mutations as state transitions whose replies matter. Test missing keys, persistent keys, conditional failures, overwrite/KEEPTTL behavior, restart/recovery, replica/failover observations when those topologies are introduced, and business behavior just before/after deadlines. Rollback plans must include how an older application version interprets changed TTL policies.

10. Summary and next step

EXPIRE and PEXPIRE set relative deadlines, EXPIREAT/PEXPIREAT set absolute deadlines, TTL/PTTL observe remaining lifetime, and PERSIST removes it. NX/XX/GT/LT make one expiry transition conditional at the server. In-place mutations and replacement writes do not all treat TTL metadata the same. Expiration makes a key unavailable after its deadline but is not an exact scheduler or complete erasure proof. Next, you will learn how to discover key families incrementally without trading correctness for a blocking KEYS scan.

Check your understanding

  1. What do TTL values -1 and -2 mean?
  2. Why can EXPIRE ... GT fail on a persistent key?
  3. Why can a plain SET create a retention bug?
  4. Does PEXPIRE guarantee memory is reclaimed at the exact configured millisecond?
  5. Why is TTL not sufficient evidence for privacy erasure?
Review the answers

1. -1 means the key exists but has no expiry. -2 means the key does not exist.

2. For GT/LT comparison a persistent key is treated as having infinite TTL, so a finite new expiry is not greater than infinity.

3. Replacing the value with SET clears an existing timeout unless KEEPTTL is requested, potentially turning expiring state into persistent state.

4. No. It expresses the deadline with millisecond precision; Redis expiration/reclamation work and observation timing do not make it an exact scheduler.

5. Persistence files, replicas, backups, logs, indexes, or other systems can retain copies. Privacy retention must cover the complete data lifecycle.

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.