168

Is it currently only possible to expire an entire key/value pair? What if I want to add values to a List type structure and have them get auto removed 1 hour after insertion. Is that currently possible, or would it require running a cron job to do the purging manually?

5 Answers 5

144

There is a common pattern that solves this problem quite well.

Use sorted sets, and use a timestamp as the score. It's then trivial to delete items by score range, which could be done periodically, or only on every write, with reads always ignoring the out of range elements, by reading only a range of scores.

More here: https://groups.google.com/forum/#!topic/redis-db/rXXMCLNkNSs

Sign up to request clarification or add additional context in comments.

5 Comments

Nice solution, but I wish redis supported this properly. It's a common requirement and shouldn't require complex workarounds.
I dunno, I'm really happy they stick to their guns - expanding the feature set in ways they don't want or don't plan means making design sacrifices. Building additional functionality on top sounds like the perfect solution, and leaves Redis to do what it does, excellently
redis is perfect as it is - simple, clean design and predictable behavior which results in more than excellent preformance
Wonderful solution, we were also able to use a simple fifo list where we kept a list and simply took the first 5 elements, and when the list grew larger than 5+N depending on scalability, we delete the key and start over. Simple, easy, and exactly how Redis algorithms should be. This algorithm gives our reporting system plenty of time to query the list and see what devices users login with. Agree with @KierenJohnstone , Redis is designed to create architecture around it and is perfect just how it is.
@let4be given that redis already supports expire for keys, I agree with UpTheCreek, it would be nice to extend the expire command to support sorted sets. That would not overcomplicate redis design nor degrade performace
85

Is it currently only possible to expire an entire key/value pair?

As far as I know, and also according to key commands and document about expiration, currently you can set expiration only to specific key and not to it's underlying data structure. However there is a discussion on google groups about this functionality with outlined alternative solutions.

5 Comments

Any idea as of now July 2016 they have supported this in there newer version?
@KamranShahid got anything on this??
Nop Prakash not yet.
Is this answer still relevant after 8 years?
Yep, element expiration in nested data structures is not supported by Redis.
5

I came upon a different method of handling this, don't know if it's helpful to any of you,but here goes:

The hash and the sorted set are linked by a guid.

  1. I have a hash that is set to expire in 'x' seconds
  2. I have a sorted set that is used for ranged queries
  3. The data for both is added in a transaction, so if one fails, they both fail.
  4. Upon a ranged query, use 'EXISTS' to see if the hashed value exists as the results are iterated over
  5. If it does not exist, it has expired, so delete the item from the sorted set

Comments

3

Since Redis 7.4 there's the command HEXPIRE which should be sufficient for most use-cases out-of-the-box.

https://redis.io/docs/latest/commands/hexpire/

As of writing this Redis 7.4 is only available in their Enterprise Software.

EDIT: 7.4 is now available in the Community Edition, implementation will generally look like this:

  1. Store the info as key:value in hash: HSET customer:1 first_name "Bob" last_order "pizza"
  2. Add key to list (LPUSH items key) or if you need scoring, to a sorted set (ZADD items score key).
  3. Set expiration: HEXPIRE key 3600 FIELDS 1 field.
  4. When getting data from list/sorted set, if key doesn't exist in hash you can safely remove it.

Example:

import redis
from time import time

r = redis.Redis(host='localhost', port=6379, decode_responses=True)

# Better to use a transaction
with r.pipeline() as pipe:
   # Store customer data in hash
   pipe.hset("customer:1", mapping={
       "first_name": "Bob", 
       "last_order": "pizza"
   })

   # Add to list (or sorted set)
   pipe.lpush("customers", "customer:1")
   # pipe.zadd("customers", {"customer:1": int(time())})  # If using sorted set

   # Set fields to expire in 1 hour
   pipe.hexpire("customer:1", 3600, "first_name", "last_order")
   
   pipe.execute()

# Later when getting customers...
customers = r.lrange("customers", 0, -1)  # For list
# customers = r.zrange("customers", 0, -1)  # For sorted set

for cust_key in customers:
   data = r.hgetall(cust_key)
   if not data:  # Hash expired
       r.lrem("customers", 0, cust_key)  # Remove from list
       # r.zrem("customers", cust_key)  # If using sorted set
   else:
       print(f"Customer: {data}")

4 Comments

Isn't this for hash keys? I don't believe it can be used for sorted sets
7.4 is now available for community edition and includes HEXPIRE. I was looking to expire items in a list which also isn't supported natively. I ended up finding the sorted set hack, and then this answer. It's not perfect but I re-worked my list into a set and am leveraging this new feature.
@DaveJohansen edited with example
@BradStell good catch!
0

What about creating two seperate sorted sets?

Main sorted set which is key = value.

Expire sorted set which is key = expire_timestamp. If you only want to expire a single score you can set as key:unique_id = expire_timestamp.

With mercy of zrangebyscore we can get expired keys. Then all we need to do is check periodically and zrem. If you only want to expire a single score: ​zincrby -1.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.