如何从Redis中自动删除过期密钥

  • 本文关键字:删除 过期 密钥 Redis redis
  • 更新时间 :
  • 英文 :


我在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中的数据没有任何引用关系!作为键的foo1和作为集合元素的foo1是两个完全不同的东西,不同的数据在不同的内存地址中,恰好存储相同的字符串"foo1"。

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

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操作。难怪这么快。

最新更新