Web应用程序的可扩展时间衰减

时间:2012-01-06 06:47:54

标签: scalability ranking-functions

我的目标是生成一个类似于reddit首页的系统。

我有东西,为了简单起见,这些东西都有选票。我生成的最好的系统是使用时间衰减。半衰期为7天,如果今天的投票价值为20分,那么在7天内,它的价值为10分,而在14天内它的价值仅为5分。

问题是,虽然这会产生结果,但我很满意,它不会扩展。每次投票都要求我有效地重新计算每次投票的价值。

所以,我认为我可以扭转这个想法。今天的投票值得1分。从现在开始的七天投票值得2分,从现在开始的14天值得4分,依此类推。这很有效,因为对于每次投票,我只需要更新一行。问题是,到今年年底,我需要一个可以拥有巨大数字的数据类型。

所以,我尝试使用线性增长产生可怕的排名。我尝试了多项式增长(自站点启动和提交以来的平方和立方数)并且它产生了稍微好一点的结果。然而,随着我获得稍微好一些的结果,我很快就会重新接近不可维护的数字。

所以,我来找你stackoverflow。谁有天才的想法或链接到如何建模这个系统的想法,以便它适用于Web应用程序。

4 个答案:

答案 0 :(得分:2)

我一直在努力做到这一点。我发现了什么看起来像一个解决方案,但不幸的是,我忘记了如何做数学,所以我无法理解它。

这个想法是存储得分的日志并按其排序,因此数字不会溢出。

这个文档描述了数学。 https://docs.google.com/View?id=dg7jwgdn_8cd9bprdr

我发现它的评论在这里: http://blog.notdot.net/2009/12/Most-popular-metrics-in-App-Engine#comment-25910828

答案 1 :(得分:0)

好的,想到在每次投票时都能做到这一点的解决方案。问题是,它需要一个链接列表,双方都有原子弹出/推送来存储投票(例如Redis列表,但你可能不希望它在RAM中)。

还要求衰减间隔恒定(例如1小时)

它是这样的:

  1. 每次投票时,将此投票的衰减次数更新到列表尾部
  2. 然后从列表头部弹出第一个投票
  3. 如果它还不够衰老,请将其推回头部
  4. 否则,从总分中减去所需的金额并将更新的信息推送到尾部
  5. 从步骤2开始重复,直到您获得足够新的投票(第3步)
  6. 当然,你仍然需要检查背景中的头部以清除当时没有人投票的帖子。

答案 2 :(得分:0)

现在已经很晚了,所以我希望有人可以查看我的数学。我认为这相当于指数衰减。

MySQL的BIGINT最大值为2 ^ 64

为简单起见,我们使用1天作为时间间隔。设n是网站推出以来的天数。

  1. 创建一个整数变量。让我们称之为X并在0
  2. 处启动它
  3. 如果添加操作会使分数超过2 ^ 64,首先,将每个分数除以2 ^ n,然后将X设置为n。
  4. 每次投票时,将2 ^(n-X)加入得分。
  5. 所以,从心理上讲,这对我使用基数10更有意义。随着我们的添加,我们的数字变得越来越长。我们停止关注较低位数的数字,因为我们增加得分的值有很多位数。这意味着较低的数字类型停止计数非常多。因此,如果它们不计算,为什么不将小数位滑动到我们关心的点并在某个点截断小数位下面的数字。为此,我们需要在每次添加的数量上滑动小数位。

    我不禁觉得这有什么问题。

答案 3 :(得分:0)

以下是您可以使用的两种可能的伪查询。我知道它们并没有真正解决可伸缩性问题,但我认为它们确实提供了方法,以便您可以

SELECT article.title AS title, SUM(vp.point) AS points
FROM article
LEFT JOIN (SELECT 1 / DATEDIFF(NOW(), vote.created_at) as point, article_id
  FROM vote GROUP BY article_id) AS vp  
ON vp.article_id = article.id

或(不是在连接中,我认为这会更快一些,但更难保湿),

SELECT SUM(1 / DATEDIFF(NOW(), created_at)) AS points, article_id
FROM vote
WHERE article_id IN (...) GROUP BY article_id

这些查询的好处是它们可以随时使用相同的数据运行,并且它们将始终返回相同的答案。它们不会破坏任何数据。

如果需要,您还可以在后台作业中运行查询,他们仍会提供相同的结果。