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

时间:2014-04-21 07:28:39

标签: redis

我有一个简单的问题,

假设服务器在10分钟内收到来自用户的10条消息,服务器会发送推送电子邮件。

起初我认为使用redis非常简单,

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

并在Java中,处理如下所示的出现次数

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

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

并且密钥到期,用户在下一分钟再次发送3条消息。

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

我们将使用Redis,我们不希望将接收时间值设置为redis。

有什么解决方案吗?

1 个答案:

答案 0 :(得分:0)

所以,有两种方法可以解决这个问题 - 一种是在空间上进行优化,另一种是在速度上进行优化(尽管速度差异应该是微不足道的)。

优化空间:

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

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中保留时间戳,但是IMO这是一个更好的解决方案,而且无论如何你都必须在时间戳上保留一些变体。

就个人而言,我采用后一种方法,因为空间差异可能不是那么大,代码可以在纯Redis中快速完成,但由你决定。