redis - 有没有更灵活地使用 redis.expire 的选项?

标签 redis

我有一个简单的问题,

假设如果服务器在 10 分钟内收到 10 条来自用户的消息,服务器将发送一封推送邮件。

一开始觉得用redis很简单,

incr("foo"), expire("foo",60*10)

在 Java 中,像下面这样处理出现次数

if(jedis.get("foo")>=10){sendEmail();jedis.del("foo");}

但想象一下,如果用户在第一分钟发送一条消息并在第 10 分钟发送 8 条消息。

key 过期,用户在下一分钟内再次发送 3 条消息。

redis key 将再次创建值为 3,即使用户实际上在 2 分钟内发送了 11 条消息,也不会触发 sendEmail()。

我们将使用 Redis,我们不想将接收时间值放入 Redis。

有什么解决办法吗?

最佳答案

因此,有两种方法可以解决此问题——一种优化空间,另一种优化速度(尽管实际上速度差异应该很小)。

空间优化:

最多保留 9 个不同的计数器; foo1 ... foo9。基本上,在我们向用户发送电子邮件之前,我们将为最多可能的 9 条不同消息中的每一条保留一个计数器,并让每条消息在达到 10 分钟标记时过期。这将像循环队列一样工作。现在执行此操作(为简单起见,在 Python 中,假设我们有一个名为 r 的 Redis 连接):

new_created = False
for i in xrange(1,10):
    var_name = 'foo%d' % i
    if not (new_created or r.exists(var_name)):
        r.set(var_name, 0)
        r.expire(var_name, 600)
        new_created = True
    if not r.exists(var_name): continue
    r.incr(var_name, 1)
    if r.get(var_name) >= 10: 
        send_email(user)
        r.del(var_name)

如果您采用这种方法,将上述逻辑放在 Lua 脚本中而不是示例 Python 中,它应该会非常快。由于每个用户最多可以存储 9 个计数器,因此它也非常节省空间。

优化速度:

为每个用户保留一个 Redis Sortet Set。每次用户发送消息时,将等于时间戳的键和任意值添加到他的排序集中。然后只需执行 ZCOUNT(now, now - 10 minutes) 并在大于 10 时发送电子邮件。然后 ZREMRANGEBYSCORE(now - 10 minutes, inf)。我知道你说过你不想在 Redis 中保留时间戳,但我认为这是一个更好的解决方案,无论如何你都必须在某个地方保留时间戳的一些变体。

我个人会选择后一种方法,因为空间差异可能不是那么大,并且代码可以在纯 Redis 中快速完成,但这取决于您。

关于redis - 有没有更灵活地使用 redis.expire 的选项?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23192926/

相关文章:

Redis 流限制

java - 我如何使用 zrangeByScore 在 SPRING-DATA-REDIS 中获取结果

node.js - 如何禁止通过 Express-session 存储的 session 的 TTL 刷新?

Javascript (NodeJS) 回调范围

ruby - EventMachine、Redis 和 EM HTTP 请求

redis - redis 中的排序集可以存储的成员数是否有上限?

caching - 如何检查redis缓存表是否为空?

java - session 是如何处理的?

node.js - 在 redis sub 上收到重复消息

php - Redis存储在数组中