Redis Python中键过期通知

26 投票
1 回答
22516 浏览
提问于 2025-04-18 08:06

我想在我的 Redis 存储中,当一个临时键过期时收到通知。Redis 的官网上有一些关于如何实现这个功能的说明,具体可以查看这个链接,但我在想,能不能通过 Python 的 Redis API 来做到这一点。

我在我的 redis.conf 文件中设置了:

notify-keyspace-events Ex

然后我进行了一个测试:

import redis
import config

client = redis.StrictRedis.from_url(config.REDIS_URI) 
client.set_response_callback('EXPIRE',callback)
client.set('a', 1)
client.expire('a',5)

但是,callback() 只在我调用 client.expire('a',5) 时被触发,而不是在五秒后像我预期的那样。

1 个回答

38

这个惊讶(当一个键的生存时间到达零时没有看到过期事件)并不是Python特有的,而是与Redis处理键过期的方式有关。

Redis关于过期事件时机的文档

过期事件的时机

带有生存时间的键在Redis中有两种过期方式:

  • 当通过命令访问这个键时,如果发现它已经过期。
  • 通过一个后台系统,这个系统会在后台逐步查找过期的键,以便能够收集那些从未被访问过的键。

过期事件是在访问一个键时,如果发现它已经过期,就会生成。因此,Redis服务器并不能保证在键的生存时间达到零时就能生成过期事件。

如果没有命令持续针对这个键,并且有很多键都有生存时间,那么从键的生存时间降到零到生成过期事件之间可能会有显著的延迟。

基本上,过期事件是在Redis服务器删除这个键时生成的,而不是在生存时间理论上达到零时生成的。

控制台的小测试

当Redis正在运行时($ sudo service redis-server start

我启动了一个控制台并进行了订阅:

$ redis-cli
PSUBSCRIBE "__key*__:*"

然后,在另一个控制台中:

$ redis-cli
> config set notify-keyspace-events AKE

我将订阅所有类型的事件

接着我在这个第二个控制台中继续进行实验:

> set aaa aaa
> del aaa
> set aaa ex 5
> get aaa

所有活动都在订阅的控制台中看到了。只有键的过期有时会延迟几秒,有时则正好及时。

还要注意,消息之间有细微的差别,一个消息是__keyevent@0__:expire,另一个是__keyevent@0__:expired

示例监听器 spy.py

import redis
import time

r = redis.StrictRedis()
pubsub = r.pubsub()
pubsub.psubscribe("*")
for msg in pubsub.listen():
    print time.time(), msg

这段代码注册了默认Redis中所有现有的频道,并打印出发布的所有内容。

运行它:

$ python spy.py

然后在另一个控制台尝试设置一个带有过期时间的键。你会看到所有的事件。

对于接下来的redis-cli输入:

$ redis-cli
127.0.0.1:6379> set a aha
OK
127.0.0.1:6379> set b bebe ex 3
OK
127.0.0.1:6379> set b bebe ex 3
OK

我们得到的监听输出:

1401548400.27 {'pattern': None, 'type': 'psubscribe', 'channel': '*', 'data': 1L}
1401548428.36 {'pattern': '*', 'type': 'pmessage', 'channel': '__keyspace@0__:a', 'data': 'set'}
1401548428.36 {'pattern': '*', 'type': 'pmessage', 'channel': '__keyevent@0__:set', 'data': 'a'}
1401548436.8 {'pattern': '*', 'type': 'pmessage', 'channel': '__keyspace@0__:b', 'data': 'set'}
1401548436.8 {'pattern': '*', 'type': 'pmessage', 'channel': '__keyevent@0__:set', 'data': 'b'}
1401548436.8 {'pattern': '*', 'type': 'pmessage', 'channel': '__keyspace@0__:b', 'data': 'expire'}
1401548436.8 {'pattern': '*', 'type': 'pmessage', 'channel': '__keyevent@0__:expire', 'data': 'b'}
1401548439.82 {'pattern': '*', 'type': 'pmessage', 'channel': '__keyspace@0__:b', 'data': 'expired'}
1401548439.82 {'pattern': '*', 'type': 'pmessage', 'channel': '__keyevent@0__:expired', 'data': 'b'}
1401548484.46 {'pattern': '*', 'type': 'pmessage', 'channel': '__keyspace@0__:b', 'data': 'set'}
1401548484.46 {'pattern': '*', 'type': 'pmessage', 'channel': '__keyevent@0__:set', 'data': 'b'}
1401548484.46 {'pattern': '*', 'type': 'pmessage', 'channel': '__keyspace@0__:b', 'data': 'expire'}
1401548484.46 {'pattern': '*', 'type': 'pmessage', 'channel': '__keyevent@0__:expire', 'data': 'b'}
1401548487.51 {'pattern': '*', 'type': 'pmessage', 'channel': '__keyspace@0__:b', 'data': 'expired'}
1401548487.51 {'pattern': '*', 'type': 'pmessage', 'channel': '__keyevent@0__:expired', 'data': 'b'}

撰写回答