Mastering Redis Cache Expiration: Commands, Lazy Deletion & Scheduled Cleanup
This article explains how Redis handles cache expiration using EXPIRE options, lazy deletion triggered by client access, and periodic cleanup tasks configured by the hz setting, while also covering implementation details, code examples, and the impact on memory management.
1 Introduction
Through previous chapters we know Redis is a KV database stored in memory, but memory is limited. To clean unnecessary data we can delete directly or set expiration. Expiration does not delete immediately; Redis removes expired data via two methods.
Lazy deletion
Periodic deletion via scheduled tasks
2 Redis Expiration Commands
We use commands to set expiration for a key; otherwise the key persists until explicitly deleted.
<code># Cache expiration command example
EXPIRE key seconds [NX|XX|GT|LT]
</code>Since Redis 7.0, EXPIRE supports options:
NX: set expiration only if the key has no expiration
XX: set expiration only if the key already has expiration
GT: set new expiration only if it is greater than the current one
LT: set new expiration only if it is less than the current one
GT, LT and NX are mutually exclusive. Official test case:
<code>redis> SET mykey "Hello"
"OK"
redis> EXPIRE mykey 10
(integer) 1
redis> TTL mykey
(integer) 10
redis> SET mykey "Hello World"
"OK"
redis> TTL mykey
(integer) -1
redis> EXPIRE mykey 10 XX
(integer) 0
redis> TTL mykey
(integer) -1
redis> EXPIRE mykey 10 NX
(integer) 1
redis> TTL mykey
(integer) 10
</code>3 Two Deletion Methods for Expired Data
Redis deletes expired data via:
Lazy deletion
Periodic deletion
3.1 Lazy Deletion
When a client accesses a key, Redis checks expiration; if expired, it deletes the key and fetches fresh data. This puts deletion responsibility on client requests, so keys that are never accessed may remain expired for a long time.
The source function expireIfNeeded implements this logic:
<code>int expireIfNeeded(redisDb *db, robj *key, int force_delete_expired) {
// Return 0 for unexpired keys
if (!keyIsExpired(db,key)) return 0;
/* Slave handling omitted for brevity */
if (server.masterhost != NULL) {
if (server.current_client == server.master) return 0;
if (!force_delete_expired) return 1;
}
if (checkClientPauseTimeoutAndReturnIfPaused()) return 1;
deleteExpiredKeyAndPropagate(db,key);
return 1;
}
</code>3.2 Periodic Deletion
Relying solely on lazy deletion is insufficient because some keys may never be accessed. Redis also runs a periodic task, frequency configured by the hz setting in redis.conf (default 10 per second).
<code># Run 10 times per second
hz 10
</code>The task randomly samples a subset of keys with expiration, checks them, and deletes those that are expired. The process:
serverCron executes based on hz .
It scans only keys that have an expiration time.
To avoid long pauses, it scans a limited number of keys per bucket (default 20).
Scanning continues across hash buckets until the limit is reached.
Expired keys found are deleted.
If more than 25% of scanned keys are expired, the cycle repeats.
Additional notes:
Scanning all keys would be costly; Redis aims to stay fast.
In cluster mode, both lazy and periodic deletions are recorded in AOF and propagated to slaves.
4 Summary
Both lazy and periodic deletions may leave some expired keys undeleted, especially if they exceed 25% of scanned keys and are never accessed, potentially exhausting memory. Redis therefore provides a comprehensive memory eviction policy, which will be covered in the next section.
Architecture & Thinking
🍠Frontline tech director and chief architect at top-tier companies 🥝 Years of deep experience in internet, e‑commerce, social, and finance sectors 🌾 Committed to publishing high‑quality articles covering core technologies of leading internet firms, application architecture, and AI breakthroughs.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.