I need to expire all keys in redis hash, which are older than 1 month.
13 Answers
This is not possible, for the sake of keeping Redis simple.
Quoth Antirez, creator of Redis:
Hi, it is not possible, either use a different top-level key for that specific field, or store along with the filed another field with an expire time, fetch both, and let the application understand if it is still valid or not based on current time.

- 4,826
- 9
- 41
- 59

- 18,572
- 3
- 31
- 36
-
26this is why redis is such an awesome piece of software. they know where to keep it simple – tObi Jan 04 '19 at 21:25
-
7@tObi IMHO it happens too often when I find out that some functionality I need in Redis is missing – Oleg Yablokov Nov 18 '21 at 20:48
-
15@tObi Disagree. Everyone is just going to implement the same feature in a variety of buggy and less efficient ways. – Trevor Hickey Feb 12 '22 at 20:30
-
@supr can you please explain why [this](https://github.com/redis/redis/issues/167#issuecomment-4442539) works? – Liran Dec 13 '22 at 13:45
-
@tObi, I want to implement a simple queue where I INCR a message ID, and then save a message with some ttl. I can't write a Lua script since clustering breaks when dynamically accessing keys in Lua scripts. I can't expire elements of a hash because yay simplicity. So what to do next? Custom sharding + Lua scripts? Use random third-party plugins and hope they are production ready? – Anish Ramaswamy Apr 14 '23 at 00:37
Redis does not support having TTL
on hashes other than the top key, which would expire the whole hash. If you are using a sharded cluster, there is another approach you could use. This approach could not be useful in all scenarios and the performance characteristics might differ from the expected ones. Still worth mentioning:
When having a hash, the structure basically looks like:
hash_top_key
- child_key_1 -> some_value
- child_key_2 -> some_value
...
- child_key_n -> some_value
Since we want to add TTL
to the child keys, we can move them to top keys. The main point is that the key now should be a combination of hash_top_key
and child key:
{hash_top_key}child_key_1 -> some_value
{hash_top_key}child_key_2 -> some_value
...
{hash_top_key}child_key_n -> some_value
We are using the {}
notation on purpose. This allows all those keys to fall in the same hash slot
. You can read more about it here: https://redis.io/topics/cluster-tutorial
Now if we want to do the same operation of hashes, we could do:
HDEL hash_top_key child_key_1 => DEL {hash_top_key}child_key_1
HGET hash_top_key child_key_1 => GET {hash_top_key}child_key_1
HSET hash_top_key child_key_1 some_value => SET {hash_top_key}child_key_1 some_value [some_TTL]
HGETALL hash_top_key =>
keyslot = CLUSTER KEYSLOT {hash_top_key}
keys = CLUSTER GETKEYSINSLOT keyslot n
MGET keys
The interesting one here is HGETALL
. First we get the hash slot
for all our children keys. Then we get the keys for that particular hash slot
and finally we retrieve the values. We need to be careful here since there could be more than n
keys for that hash slot
and also there could be keys that we are not interested in but they have the same hash slot
. We could actually write a Lua
script to do those steps in the server by executing an EVAL
or EVALSHA
command. Again, you need to take into consideration the performance of this approach for your particular scenario.
Some more references:

- 6,725
- 7
- 54
- 78
-
3
-
I am fine with expiring the top key and with it all the sub keys ? however can not find how to expire the entire hash with all the keys as well. – mjs Jun 26 '21 at 16:50
-
@mjs you can just set the expire on it. `EXPIRE myHashKey 10` expires your hash in 10 seconds. https://redis.io/commands/expire – jonlink Dec 20 '21 at 13:47
-
@jonlink that looks relevant to setting a key, not a key that belongs to a hash. edit. i see that was what I wanted :p ... i think the problem is perhaps it is not ttl is not updated when you add things to the hash ? – mjs Dec 20 '21 at 18:30
-
This is possible in KeyDB which is a Fork of Redis. Because it's a Fork its fully compatible with Redis and works as a drop in replacement.
Just use the EXPIREMEMBER command. It works with sets, hashes, and sorted sets.
EXPIREMEMBER keyname subkey [time]
You can also use TTL and PTTL to see the expiration
TTL keyname subkey
More documentation is available here: https://docs.keydb.dev/docs/commands/#expiremember

- 239
- 2
- 2
You can use Sorted Set in redis to get a TTL container with timestamp as score.
For example, whenever you insert a event string into the set you can set its score to the event time.
Thus you can get data of any time window by calling
zrangebyscore "your set name" min-time max-time
Moreover, we can do expire by using zremrangebyscore "your set name" min-time max-time
to remove old events.
The only drawback here is you have to do housekeeping from an outsider process to maintain the size of the set.

- 641
- 1
- 6
- 9
We had the same problem discussed here.
We have a Redis hash, a key to hash entries (name/value pairs), and we needed to hold individual expiration times on each hash entry.
We implemented this by adding n bytes of prefix data containing encoded expiration information when we write the hash entry values, we also set the key to expire at the time contained in the value being written.
Then, on read, we decode the prefix and check for expiration. This is additional overhead, however, the reads are still O(n) and the entire key will expire when the last hash entry has expired.

- 1,155
- 1
- 13
- 20
The solution I've been come up with:
Lets say I want to expire every 3 minutes: So im holding the data in 3 fields 0 1 2. and then i do module% 3 to current time in minutes.
if the module for example == 0 so im using only 1 2 and 0 i delete; then it change to 1 so im using 2 and 0 and delete 1.
Im not using it and i didnt checked it but im just let you know its possible

- 14,701
- 2
- 25
- 31

- 71
- 1
- 3
There is a Redisson java framework which implements hash Map
object with entry TTL support. It uses hmap
and zset
Redis objects under the hood. Usage example:
RMapCache<Integer, String> map = redisson.getMapCache('map');
map.put(1, 30, TimeUnit.DAYS); // this entry expires in 30 days
This approach is quite useful.

- 8,362
- 6
- 61
- 88

- 10,283
- 1
- 62
- 71
-
but how can you create a map? because I do not find any tutorial or a create/set method – FaNaT Aug 26 '16 at 02:07
-
@ZoltánNémeth In Redis map created automatically when first value inserted. – Nikita Koksharov Aug 26 '16 at 10:12
If your use-case is that you're caching values in Redis and are tolerant of stale values but would like to refresh them occasionally so that they don't get too stale, a hacky workaround is to just include a timestamp in the field value and handle expirations in whatever place you're accessing the value.
This allows you to keep using Redis hashes normally without needing to worry about any complications that might arise from the other approaches. The only cost is a bit of extra logic and parsing on the client end. Not a perfect solution, but it's what I typically do as I haven't needed TTL for any other reason and I'm usually needing to do extra parsing on the cached value anyways.
So basically it'll be something like this:
In Redis:
hash_name
- field_1: "2021-01-15;123"
- field_2: "2021-01-20;125"
- field_2: "2021-02-01;127"
Your (pseudo)code:
val = redis.hget(hash_name, field_1)
timestamp = val.substring(0, val.index_of(";"))
if now() > timestamp:
new_val = get_updated_value()
new_timestamp = now() + EXPIRY_LENGTH
redis.hset(hash_name, field_1, new_timestamp + ";" + new_val)
val = new_val
else:
val = val.substring(val.index_of(";"))
// proceed to use val
The biggest caveat imo is that you don't ever remove fields so the hash can grow quite large. Not sure there's an elegant solution for that - I usually just delete the hash every once in a while if it feels too big. Maybe you could keep track of everything you've stored somewhere and remove them periodically (though at that point, you might as well just be using that mechanism to expire the fields manually...).

- 2,280
- 2
- 22
- 37
Regarding a NodeJS implementation, I have added a custom expiryTime
field in the object I save in the HASH. Then after a specific period time, I clear the expired HASH entries by using the following code:
client.hgetall(HASH_NAME, function(err, reply) {
if (reply) {
Object.keys(reply).forEach(key => {
if (reply[key] && JSON.parse(reply[key]).expiryTime < (new Date).getTime()) {
client.hdel(HASH_NAME, key);
}
})
}
});

- 8,362
- 6
- 61
- 88

- 205
- 2
- 4
-
You can make this more efficient by using `Array.filter` to create an array of `keys` to delete from the hash then pass that to `client.hdel(HASH_NAME, ...keys)` in a single call. – doublesharp Jan 01 '19 at 18:50
-
`const keys = Object.keys(reply).filter(key => reply[key] && JSON.parse(reply[key]).expiryTime < Date.now()); client.hdel(HASH_NAME, ...keys);` – doublesharp Jan 01 '19 at 18:54
You could store key/values in Redis differently to achieve this, by just adding a prefix or namespace to your keys when you store them e.g. "hset_"
Get a key/value
GET hset_key
equals toHGET hset key
Add a key/value
SET hset_key value
equals toHSET hset key
Get all keys
KEYS hset_*
equals toHGETALL hset
Get all vals should be done in 2 ops, first get all keys
KEYS hset_*
then get the value for each keyAdd a key/value with TTL or expire which is the topic of question:
SET hset_key value
EXPIRE hset_key
Note: KEYS
will lookup up for matching the key in the whole database which may affect on performance especially if you have big database.
Note:
KEYS
will lookup up for matching the key in the whole database which may affect on performance especially if you have big database. whileSCAN 0 MATCH hset_*
might be better as long as it doesn't block the server but still performance is an issue in case of big database.You may create a new database for storing separately these keys that you want to expire especially if they are small set of keys.
Thanks to @DanFarrell who highlighted the performance issue related to
KEYS

- 21,644
- 6
- 109
- 75
-
2just be aware this is a significant change to performance characteristics – erik258 Feb 05 '19 at 20:48
-
how! if you talking about time complexities, all these operations have the same time complexity as `hashset` .. get O(1) set O(1) get all O(n) – Muhammad Soliman Feb 06 '19 at 04:55
-
2"consider KEYS as a command that should only be used in production environments with extreme care." https://redis.io/commands/KEYS . HGETALL is `O(n)` for number of things in the set, `KEYS` to number of things in the DB. – erik258 Feb 06 '19 at 17:57
-
that's true, `scan 0 match namespace:*` might be better as long as it doesn't block the server – Muhammad Soliman Feb 07 '19 at 02:40
-
1also, separate these keys, if they're small set, to different database. thanks @DanFarrell – Muhammad Soliman Feb 07 '19 at 02:56
-
1scan is also O(n), this turns HGETALL from O(size_of_hash) to O(size_of_entire_redis_database) – Binyamin Sep 06 '20 at 07:17
-
1if you have a few 100K keys, then you will find SCAN, while not blocking other calls for long, will see your REDIS CPU hit 100% – dan carter Sep 30 '20 at 11:25
You can. Here is an example.
redis 127.0.0.1:6379> hset key f1 1
(integer) 1
redis 127.0.0.1:6379> hset key f2 2
(integer) 1
redis 127.0.0.1:6379> hvals key
1) "1"
2) "1"
3) "2"
redis 127.0.0.1:6379> expire key 10
(integer) 1
redis 127.0.0.1:6379> hvals key
1) "1"
2) "1"
3) "2"
redis 127.0.0.1:6379> hvals key
1) "1"
2) "1"
3) "2"
redis 127.0.0.1:6379> hvals key
Use EXPIRE or EXPIREAT command.
If you want to expire specific keys in the hash older then 1 month. This is not possible. Redis expire command is for all keys in the hash. If you set daily hash key, you can set a keys time to live.
hset key-20140325 f1 1
expire key-20140325 100
hset key-20140325 f1 2

- 206
- 1
- 3
-
29I don't think he wants to expire *all* keys in the hash, rather *all keys in the hash which are older than 1 month*, so only some of them. Which AFAIK is not possible. – UpTheCreek Jul 23 '13 at 21:08
-
2IMHO the question let us assume this. So Jonathan gets +1 from me, cos he helped me out! Thanx! – Chris W. Nov 18 '13 at 17:28
-
-
6This does not answer the question or helps at all. The need is to expire the elements in the hash, not the hash itself. – Ron Jan 23 '14 at 12:20
You could use the Redis Keyspace Notifications by using psubscribe
and "__keyevent@<DB-INDEX>__:expired"
.
With that, each time that a key will expire, you will get a message published on your redis connection.
Regarding your question basically you create a temporary "normal" key using set
with an expiration time in s/ms. It should match the name of the key that you wish to delete in your set.
As your temporary key will be published to your redis connection holding the "__keyevent@0__:expired"
when it expired, you can easily delete your key from your original set as the message will have the name of the key.
A simple example in practice on that page : https://medium.com/@micah1powell/using-redis-keyspace-notifications-for-a-reminder-service-with-node-c05047befec3
doc : https://redis.io/topics/notifications ( look for the flag xE)

- 30,039
- 14
- 71
- 73
static async setCount(ip: string, count: number) {
const val = await redisClient.hSet(ip, 'ipHashField', count)
await redisClient.expire(ip, this.expireTime)
}
Try expire your key.

- 1