【问题标题】:How to automatically remove an expired key from a set in Redis?如何从 Redis 的集合中自动删除过期的密钥?
【发布时间】:2015-10-02 11:48:41
【问题描述】:

比如说,我在 Redis 数据库中有 4 个键。从现在开始,密钥的有效期为 10 秒。我已将密钥添加到集合中。当密钥过期时,它们实际上不再存在于数据库中(get 返回空值)。但是,键仍然是集合的成员。该集合将继续存在,直到从集合中删除键,此时它太有效地从数据库中消失了。

是否可以在密钥过期时自动从其所属的集合中删除密钥?

我正在考虑为每个键维护一个集合以维护它所属的集合(在键和集合之间建立有向图集合关系),然后注册键过期事件以在必要时删除集合成员。作为数据库的消费者,而不是作为内部的某种后台清理线程,这是很多开销。而且,这种方法是清理集合的“最大努力”,因为订阅过期事件的消费者代码可能会崩溃、无法收到通知、积压等。

我可以通过将键集建模为哈希集中的字段来避免任何欺骗,但我希望在实践中每个键具有不同的到期 TTL。如果可以,那又如何呢?

例如,这是我的一组“foo”键。

mine:0>set foo1 barA
OK

mine:0>set foo2 barB
OK

mine:0>set foo3 barC
OK

mine:0>set foo4 barD
OK

它们可以添加到集合中。

mine:0>sadd foo foo1 foo2 foo3 foo4
4

mine:0>smembers foo
1) foo1
2) foo3
3) foo4
4) foo2

然后密钥可以过期...

mine:0>expire foo1 10
1

mine:0>expire foo2 10
1

mine:0>expire foo3 10
1

mine:0>expire foo4 10
1

mine:0>get foo1 
NULL

mine:0>get foo2
NULL

mine:0>get foo3
NULL

mine:0>get foo4
NULL

mine:0>get foo5
NULL

此时键不存在,但集合存在,并且它引用了键。

mine:0>smembers foo
1) foo1
2) foo3
3) foo4
4) foo2

显式移除键会将它们从集合中移除,然后在全部移除后使集合不存在。

mine:0>srem foo foo1
1

mine:0>srem foo foo2
1

mine:0>srem foo foo3
1

mine:0>srem foo foo4
1

mine:0>smembers foo
[nothing returned]

【问题讨论】:

  • 你为什么不在集合上设置一个过期时间,与那里添加的密钥相同或最小/最大?

标签: redis


【解决方案1】:

很简单!

首先,我想指出redis中的数据没有任何引用关系! foo1 作为 key 和 foo1 作为 set 中的元素是两个完全不同的东西,不同的内存地址中的不同数据,恰好存储了相同的字符串“foo1”。

第二,我的方式不需要改变你的数据模型,就像你查询你的集合一样。查询set中的元素时,检查key是否在redis中也作为单独的key存在,逻辑如下:

ArrayList getUnexpiredElementsInSet(String setKey){
            ArrayList elements = smembers(setKey);
            ArrayList resultArray = new ArrayList();
            for (element in elements) {
                if (exists(element)==false) {
                    srem(setKey, element);
                }else {
                    resultArray.add(element);
                }
            return resultArray;
          }

这样当你想查询一个集合中的东西时,你只会得到未过期的元素,并且过期的元素也会被清除。

以上只是伪 java 代码向您展示基本逻辑,您需要将此逻辑包装在 lua 事务中。如果你不知道redis的lua特性,那就学吧!它很棒,让 redis 超级快速和强大。要想发挥redis的真正威力,lua是必不可少的!参考Redis Lua

有些人担心脚本命令的性能损失。我可以向你保证,这是不必要的。根据官方文档和我在实践中的经验,运行脚本根本没有性能损失。 这在理论上可以从很多方面理解

  1. Redis 采用 Lua 而不是任何其他语言,因为 Lua 声称是最快的脚本语言。
  2. 每个脚本都可以存储在服务器端,客户端只需发送该脚本的 sha1 值及其输入参数即可调用,因此不存在长脚本的 I/O 负担。
  3. 通过发送存储脚本的sha1运行脚本时,脚本在redis服务器上进行预编译。因此,通过这种方式,运行包含在脚本中的命令基本上与以普通旧形式运行这些命令相同,只是脚本节省了大量耗时的 I/O 操作。难怪它超级快。

【讨论】:

  • 这个不错。关于 key 和 set-member 在物理上是不同的数据片段的优点。这就是问题的核心。使用获取未过期成员,删除过期成员的方法,如果在所有密钥都过期后不使用集合,则集合可能会永远存在。因此,为了确保内存增长是有限的,需要有一些东西可以定期检查每组。偷偷地,我希望有人有办法在服务器上运行 lua 脚本作为事件挂钩。虽然,据我所知,运行脚本会对性能造成很大影响。
猜你喜欢
  • 1970-01-01
  • 2011-02-22
  • 1970-01-01
  • 2015-12-18
  • 1970-01-01
  • 2022-01-07
  • 2017-10-08
  • 1970-01-01
  • 2015-08-14
相关资源
最近更新 更多