如何使用Redis实现临时领导者?

时间:2014-09-07 03:53:42

标签: redis

使用Redis,实现具有排序集的记分板是微不足道的,但我不确定如何使用滚动时间窗口(即30/60/90天窗口)实现记分板。

由于排序集条目没有任何时间组件,实现基于时间的排行榜的最佳方法是什么?

1 个答案:

答案 0 :(得分:2)

以下是您可以考虑的两种常规方法(即需要填写的详细信息;)

1)使用Redis的ZSET(有序集)的分数性质来存储时间和用户的(?)分数(例如,整数部分可以是时间戳和分数的分数)。时间戳部分的范围可以为您提供时间效果,但是对于排序,您必须重置历元值的“LSB”。

2)使用不同的引线板,每个滚动窗口一个,定期或在触摸数据库时保持它们。

第一种方法更简单但你可能会遇到极端情况(例如浮点数据类型的限制,巨大的ZSET ......),所以如果这是一个问题,你应该考虑提前对排行榜进行分区。

编辑 - 更多示例: 我们假设您的排行榜的密钥是k,并且您正在跟踪term1。天真地,当第1项命中时,你会这样做:

ZADD k <epoch> <epoch>:term1

这会给你每个术语命中1秒的分辨率但是你需要更多,并且没有必要以这种方式保持计数。所以,我们假设<epoch*>总是在上午12点。而不是计算每个术语独立命中,而是像这样聚合:

escore = ZSCORE k:<epoch*> term1
if (escore == nil):
    escore = 0

ZADD k:<epoch*> escore+1 term1

要在滚动窗口中聚合,例如30天,确定k:<epoch*>:30d<epoch*><epoch*> - 30天之间所有天的汇总。因此,每次term1点击时,您都会执行以下操作:

# initialize today's rolling window if it doesn't exist
if not(EXISTS k:<epoch*>:30d):
    ZUNIONSTORE k:<epoch*>:30d 29 k:<epoch*>-1d ... k:<epoch*>-29d AGGREGATE SUM

rscore = ZSCORE k:<epoch*>:30d term1
if (rscore == nil):
    rscore = 0

ZADD k:<epoch*>:30d rscore+1 term1

这实际上是我对第二种方法的意思,所以是的,你将保留30个键(只需记住在不再需要它们时删除/过期)。

第一种方法包括使用单个k ZSET进行所有操作。假设您的术语计数最多可达10000,请考虑以下伪计数,即每个术语计数和汇总:

escore = ZSCORE k <epoch*>:term1
rscore = ZSCORE k <epoch*>:30d:term1
if (escore == nil):
    escore = <epoch*>

if (rscore == nil):
    rscore = <epoch*>
    for (i=1; i++; i<30):
        rscore += fractional(ZSCORE k <epoch* - i*days>:term1)

ZADD k escore+1/10000 <epoch*> + ':term1'
ZADD k rscore+1/10000 <epoch*> + ':30d:term1'

此设计使用单个k来存储所有汇总,其中分数的整数部分是纪元(允许您执行范围),小数部分是计数器(%10000)。