Here is the feature that turns Redis from a dictionary into a cache: Redis key expiration lets a key delete itself after a set number of seconds. You store something, put a timer on it, and Redis quietly removes it when the timer runs out. No cleanup job, no cron, no code.
Why self-deleting keys matter for caching
Cached data goes stale. A product price, a rendered page, a currency rate - you store it to avoid recomputing it, but you don't want to serve a week-old copy forever. The classic answer is: "keep this for 60 seconds, then throw it away and fetch fresh data."
That timer is called the TTL - time to live. It is the beating heart of almost every caching strategy, and the rest of this course leans on it heavily.
Setting an expiry with EXPIRE
Store a key the normal way, then attach a TTL in seconds with EXPIRE:
SET session:abc "logged-in"
EXPIRE session:abc 60
OK
(integer) 1
The (integer) 1 means "yes, the expiry was set". From now on, this key will vanish 60
seconds after you set the timer. One catch worth knowing early: EXPIRE only works on a
key that already exists. Run it against a missing key and you get (integer) 0 and no
timer is created - there is nothing to attach the clock to.
Reading a key's TTL
Ask how much time a key has left with TTL:
TTL session:abc
(integer) 55
Fifty-five seconds left. Run it again a moment later and the number will be lower. Two special replies are worth knowing:
(integer) -2 the key does not exist (gone, or never existed)
(integer) -1 the key exists but has no expiry (it lives forever)
So -1 means "permanent" and -2 means "already gone". Beginners often confuse these
two - remember -2 is more negative because the key is more gone.
Setting value and expiry together with SETEX
Doing SET then EXPIRE as two commands has a subtle risk: if something interrupts you
between them, the key could end up with no timer and live forever. Redis gives you two
ways to set the value and the TTL together, safely.
SETEX takes the seconds in the middle:
SETEX page:home 30 "<h1>Home</h1>"
TTL page:home
OK
(integer) 30
The modern, more flexible form is SET with the EX option:
SET page:home "<h1>Home</h1>" EX 30
OK
Both store the value and the 30-second TTL in a single atomic command. Prefer these
over a separate EXPIRE whenever you know the lifetime up front - which, for a cache, is
almost always.
Removing an expiry with PERSIST
Changed your mind? PERSIST strips the timer off a key and makes it permanent again:
SET temp "keep me" EX 100
PERSIST temp
TTL temp
OK
(integer) 1
(integer) -1
The TTL is now -1: the key exists and will no longer expire.
Why overwriting a key clears its TTL
This one bites people. If a key has a TTL and you overwrite it with a plain SET, the
timer is wiped and the key becomes permanent:
SET item "v1" EX 60
SET item "v2"
TTL item
OK
OK
(integer) -1
The second SET replaced the value and threw away the expiry. If you want the new value
to keep expiring, set the TTL again in the same command: SET item "v2" EX 60. (Commands
like INCR, which you will meet in a later lesson, do not clear the TTL - only a full
SET does.)
Common mistake: forgetting the TTL
The most common expiry bug is forgetting the TTL entirely. You add a cache, ship it, and
weeks later Redis is full of keys that never die because every write was a plain SET.
Memory creeps up until Redis starts evicting or refusing writes. Rule of thumb: a cache
key should almost always be born with a TTL. Use SET ... EX or SETEX so the timer is
never an afterthought.
FAQ
Does the TTL count down only while Redis is running?
TTL is based on wall-clock time, not uptime. If a key has 60 seconds left and Redis is restarted, then comes back two minutes later, the key is already expired and gone. The clock does not pause.
Can I set a TTL in milliseconds?
Yes. Use PEXPIRE for milliseconds, PTTL to read the remaining milliseconds, and
SET ... PX 500 to set the value with a 500-millisecond TTL. Seconds cover most caching
needs, but the millisecond versions exist when you need finer control.
What exactly happens the moment a key expires?
It is treated as deleted. GET returns (nil), EXISTS returns 0, and TTL returns
-2. Redis removes expired keys both lazily (when you touch them) and in the background,
so you never have to clean them up yourself.