在 Redis 中获取单个元素在 30 天内过期的集合的最干净方法?

Cleanest Way to Get a Set in Redis where Individual Elements Expire in 30 Days?

提问人:Della 提问时间:11/10/2023 更新时间:11/11/2023 访问量:48

问:

我需要一个类似于 redis 中 Python 集的数据结构,并具有单个元素在插入 30 天后自动从集合中过期(弹出)的附加功能。基本上,这些是我想要从课堂上得到的抽象行为。

from abc import abstractmethod, ABC
from typing import Iterable 

class RedisSet(ABC):
    """Implement a set of strings with a remote redis host."""
    @abstractmethod
    def __init__(self, url:str):
        """
           Initialise the class at the remote redis url.
           The set of strings should be empty initially.            
        """
        raise NotImplementedError    
    @abstractmethod
    def add(self, new_elements:Iterable[str])->None:
        """Insert all the elements into the set."""
        raise NotImplementedError
    @abstractmethod
    def __contains__(self, elem:str)->bool:
        """Check membership."""
        raise NotImplementedError

那么,实现它的最干净的方法是什么?我不需要实现整个类,而是询问在 redis 中使用的正确数据类型和 API 是什么,因为我并不完全熟悉 redis 的全部功能。

我注意到 redis 有一个固定的数据类型,但似乎(如果我错了,很高兴得到纠正)它不支持任何生存时间 (TTL)。相反,字典支持 TTL,但我必须为每个键使用占位符(不必要的开销),并且我不确定成员资格检查是否是恒定时间。value

注意

  • 我不认为需要遍历这些元素,因此针对该操作进行优化是多余的。我所需要的只是会员资格检查。
  • 我打算用作python客户端。它应该支持必要的 redis 操作。aioredis

任何关于正确的 redis 数据类型的想法,我应该查找其文档,我们将不胜感激。

python redis set hashset aioredis

评论


答:

1赞 Ali Malek 11/11/2023 #1

为了实现您想要的类似功能,您应该考虑存在限制,并且最终解决方案可能看起来很复杂。RedisRedis

有什么限制?

正如您正确提到的,不提供 HASH、SETS 或任何其他数据类型的功能。只需支持简单的键/值。RedisTTL

解决方案是什么?

可以使用 SortedSet。此数据类型获取您添加到其中的每个元素的分数,在本例中为过期时间。 您需要维护按代码排序的集合,以从中删除过期的元素。您可以使用调度程序实现此部分。

添加新元素

ZADD my-set 1699631227694 elm1
(integer) 1
ZADD my-set 1699631228234 elm2 1699631228234 elm3
(integer) 2
ZADD my-set 1699631229123 elm1
(integer) 0

(integer) ${number} - ${number}确定您添加的元素数量,如果得到 0 或小于输入元素,则表示您刚刚更新了元素的分数。

检查存在状态

现在你可以通过这个命令检查一个键的存在,注意你需要验证elememnt没有过期,所以在你的代码中需要用分数检查时间。

ZSCORE my-set elm1 // which retuen the score of elm1
"1699631227694"
ZSCORE my-set elm4
(nil)

(nil)表示该元素不存在

删除过时的元素

这是由 your schaduler 运行的,以删除从sorted set

ZREMRANGEBYSCORE my-set -inf 1699631228234
(integer) 2

不幸的是,我不是 Python 开发人员,无法帮助您编写代码。 但我认为这段代码可以帮助您获得洞察力:

from abc import abstractmethod, ABC
from typing import Iterable 

class RedisSet(ABC):
    """Implement a set of strings with a remote redis host."""
    @abstractmethod
    def __init__(self, url:str):
        """
           Initialise the class at the remote redis url.
           The set of strings should be empty initially.            
        """
        self.redis = // init the redis connection
        self.set_key = set_key
        
    @abstractmethod
    def add(self, new_elements:Iterable[str])->None:
        """Insert all the elements into the set."""
        const startTime = Date.now();
        const offset = 1000*60*60*24*30; // 30 days
        
        for elem in new_elements:
            await self.redis..zadd(self.set_key, Date.now() + offset, elem)

    @abstractmethod
    def __contains__(self, elem:str)->bool:
        """Check membership."""
        exists = await self.redis.zscore(self.set_key, elem)

        if score is None or score < time.time():
            return False
        
        return True

评论

1赞 Della 11/12/2023
In that case, it seems using a dictionary of key value pairs (which supports auto expiry of keys) is a better choice. But I want to know, what's the most memory efficient sentinel value that I can assign to each key when I am not at all interested in the value, but merely in the keys' existence?
1赞 Ali Malek 11/13/2023
@Della Your solution is simpler, but don't forget you are using extra memory by using the expire feature. it is a feature that Redis needs to keep some data about the time of expiry and the key name. So I assume that your solution would use a bit more memory. It would be better to implement an MVP of these solutions and check their performance and memory usage. PS. You can put an empty string for value in your solution, and it would be minimal in size. PS. For checking the memory You can use . PS. A command for checking the serialization length INFO MemoryDEBUG OBJECT key