KAHIBARO
Discord Login Register

16.3.4. Expiration

Why Expiration Matters in Redis

Redis is often used for caching, sessions, rate limiting, and many other features that depend on temporary data. Expiration is how Redis automatically deletes keys after some time.

If you set expirations correctly, you avoid:

If you ignore expiration, Redis memory usage can grow without bound and your app might behave incorrectly.

In this chapter we focus on how key expiration works in Redis, how to set and inspect TTLs, and common patterns used in backend development.

Ways to Set Expiration on Keys

Redis lets you set a time to live (TTL) on keys in several ways:

You can specify expiration in:

Below are the main commands and how to use them.

Setting Expiration After Creating a Key

The most basic pattern is:

  1. Create or update a key.
  2. Apply expiration.
bash
SET session:user:42 "some session data"
EXPIRE session:user:42 1800   # 1800 seconds = 30 minutes

EXPIRE sets time to live in seconds.

To use milliseconds instead:

bash
PEXPIRE session:user:42 600000   # 600000 ms = 600 s = 10 minutes

To set expiration as an absolute timestamp using UNIX time in seconds:

bash
# expire at a specific timestamp (e.g. 1730000000)
EXPIREAT session:user:42 1730000000

For millisecond timestamps:

bash
PEXPIREAT session:user:42 1730000000000

Return Values of Expiration Commands

Commands like EXPIRE, PEXPIRE, EXPIREAT, PEXPIREAT return:

Example:

bash
EXPIRE missing:key 60
(integer) 0

Setting Expiration When Creating a Key

Often you want to create a cache entry or a temporary value with an expiration in a single command.

You can use SET with extra options.

bash
SET cache:user:42 '{"name":"Alice"}' EX 300
# EX 300 = expire in 300 seconds

Some useful options:

OptionMeaningExample
EX secondsSet expiration in secondsSET k v EX 60
PX millisecondsSet expiration in msSET k v PX 1500
NXOnly set if key does not existSET k v NX EX 60
XXOnly set if key existsSET k v XX EX 60

Combining them for distributed locks or atomic operations:

bash
# Attempt to create a lock with 10 second TTL, only if it does not exist
SET lock:order:123 "random-token" NX EX 10

That gives you:

Checking and Removing Expiration

Once a key has an expiration, you often want to:

Checking TTL of a Key

Use TTL to check remaining life in seconds.

bash
TTL session:user:42

Possible results:

ResultMeaning
> 0Key will expire in that many seconds
-1Key exists but has no expiration (persistent)
-2Key does not exist

Example walk-through:

bash
SET cache:item:1 "hello" EX 30
TTL cache:item:1
(integer) 27   # for example, after 3 seconds
DEL cache:item:1
TTL cache:item:1
(integer) -2   # key is gone

For milliseconds you can use PTTL:

bash
PTTL cache:item:1
(integer) 25311   # 25.311 seconds remaining

Removing Expiration (Persisting a Key)

If you decide that a key should not expire any more, use PERSIST:

bash
SET temp:data "value" EX 60
TTL temp:data
(integer) 60
PERSIST temp:data
(integer) 1          # expiration removed
TTL temp:data
(integer) -1         # key is now persistent

Return codes of PERSIST:

How Redis Actually Expires Keys

Redis expiration is not exact to the millisecond. It tries to be efficient with CPU and memory.

There are two important concepts:

  1. Passive expiration
    When a client accesses an expired key, Redis notices and deletes it on the spot.
  2. Active expiration
    Redis periodically scans a sample of keys that have an expiration set and removes the ones that have passed their time.

You do not control these internals directly, but you must understand the implications:

Important rule:
You should never rely on exact expiration times down to milliseconds. Treat Redis expiration as approximately correct, not as a precise timer mechanism.

Keyspace Notifications for Expiration

Sometimes backends need to react when a key expires. For example:

Redis supports keyspace notifications, which can publish events when keys expire.

To enable expiration events:

bash
CONFIG SET notify-keyspace-events Ex

Here:

Then you can subscribe to those events (from a client or redis-cli):

bash
PSUBSCRIBE "__keyevent@0__:expired"

Example scenario:

  1. You set expiration on a session key:
bash
   SET session:user:42 "..." EX 1800
  1. After 1800 seconds, Redis expires the key.
  2. Redis publishes a message on the pattern __keyevent@0__:expired with the expired key name.

Client libraries for many languages allow you to use this for event-driven logic, but remember:

Common Expiration Patterns in Backend Development

Redis expiration is central to many backend use cases. Here are several common patterns and concrete examples.

Cache with TTL

A typical pattern: store a computed value for a limited time.

Example: cache user profile details for 5 minutes.

bash
SET cache:user:42 '{"id":42,"name":"Alice"}' EX 300

Your backend logic often follows this pattern:

  1. Try GET cache:user:42.
  2. If it exists, return the cached JSON.
  3. If not, load from the database, then store it with EX and return it.

This keeps the cache bounded in size and prevents old data from living forever.

Sometimes you want shorter TTLs for fast changing data, and longer TTLs for stable data. For example:

KeyTTL
cache:user:42300 s
cache:product:1233600 s
cache:category:list86400 s

Sessions with Sliding Expiration

User sessions should expire after a period of inactivity, not just from login time.

Imagine a session that should expire after 30 minutes of inactivity. Each time the user performs a request, you refresh the expiration.

Backend logic:

  1. When user logs in:
bash
   SET session:user:42 "session-data" EX 1800
  1. On each request with a valid session:
bash
   # Option A: Rewrite value and TTL
   SET session:user:42 "session-data" EX 1800
   # Option B: Use EXPIRE to refresh TTL
   EXPIRE session:user:42 1800

The session key will disappear automatically 30 minutes after the last request.

Important rule:
For sliding sessions, always refresh TTL on user activity. If you only set TTL at login, active users will be logged out unexpectedly.

Rate Limiting Using Expiration

Expiration is central to simple rate limiting. For example, limit a user to 100 requests per 60 seconds.

One strategy:

Example:

bash
INCR rate:user:42
EXPIRE rate:user:42 60

In practice, you must make this safe against race conditions. A better version in one step:

bash
# Pseudocode using a Lua script or your language client:
# if key exists:
#   INCR
# else:
#   SET key 1 EX 60

Rough logic:

After 60 seconds, Redis expires the counter key by itself.

Temporary Tokens and One-Time Codes

For things like:

You want a code that works only for a short time.

Example: password reset token valid for 15 minutes.

bash
SET reset:token:abc123 user:42 EX 900

Backend flow:

  1. When user requests password reset, generate abc123, store mapping reset:token:abc123 -> user:42 with EX 900.
  2. When user clicks the link with abc123, your backend does:
bash
   GET reset:token:abc123
  1. If key exists, token is valid, proceed with reset and delete the key:
bash
   DEL reset:token:abc123
  1. If key is missing, TTL expired, or key never existed, reject the request.

Here expiration is your automatic safety net. Tokens cannot be reused after the time window.

Memory Management and Expiration

Expiration and memory policy are related but different.

If you configure Redis with a memory limit, and use many expiring keys, you need to understand that:

This does not usually break application logic if your code is written to handle missing keys gracefully.

For caching, eviction is usually fine, because a missing key just means "recompute and store again".

Time Units and Timestamps

It is important to be clear about seconds vs milliseconds and about relative vs absolute time.

CommandInput typeUnit
EXPIRERelative time from nowSeconds
PEXPIRERelative time from nowMilliseconds
EXPIREATAbsolute UNIX timestampSeconds
PEXPIREATAbsolute UNIX timestampMilliseconds
TTLRemaining timeSeconds
PTTLRemaining timeMilliseconds

You can think of relative expiration time as:

$$
t_{\text{expire}} = t_{\text{now}} + \Delta t
$$

Where:

Absolute expiration commands directly set $t_{\text{expire}}$ as an absolute UNIX timestamp you choose.

Good Practices for Using Expiration in Backends

A few practical guidelines:

  1. Always set TTL for cache keys
    Caches should not be permanent. Use a sensible TTL based on how often underlying data changes.
  2. Name keys clearly
    Include semantics in key names so you know what is temporary. For example: cache:, session:, rate:, reset:.
  3. Handle missing keys as normal
    In code, always expect that a key might not exist or might have expired. This should be a normal, non-exceptional path.
  4. Avoid using Redis expiration as a precise scheduler
    If you need task scheduling or delayed jobs with guarantees, use a proper job queue or scheduler. Redis expiration is approximate.
  5. Be consistent with time units
    Choose whether your project uses seconds or milliseconds for most operations, and stick to it to avoid confusion.
  6. Be cautious when refreshing TTL
    For some keys, you want fixed lifetime from creation, not from last access. If you call EXPIRE every time you read the key, you change the semantics to sliding expiration.

By understanding and correctly using expiration, you can keep your Redis instance healthy, control memory usage, and implement many useful backend features such as caching, sessions, rate limiting, and temporary tokens.

Views: 7

Comments

Please login to add a comment.

Don't have an account? Register now!