【问题标题】:Scalable time decay for web applicationWeb 应用程序的可扩展时间衰减
【发布时间】:2012-01-06 06:47:53
【问题描述】:

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

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

问题是,虽然这会产生我非常满意的结果,但它并不能扩展。每一次投票都需要我有效地重新计算其他投票的价值。

所以,我想我也许可以扭转这个想法。今天的投票值 1 分。从现在起 7 天后的投票值 2 分,从现在起 14 天后的投票值 4 分,以此类推。这很有效,因为每次投票,我只需要更新一行。问题是,到今年年底,我需要一种可以容纳大量数字的数据类型。

所以,我尝试使用产生糟糕排名的线性增长。我尝试了多项式增长(将网站启动和提交后的天数平方和立方),它产生了稍微好一点的结果。但是,随着我得到稍微好一点的结果,我很快就会重新接近无法维护的数字。

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

【问题讨论】:

  • seomoz.org/blog/… 有点帮助,但乍一看,似乎也没有一个扩展。
  • 我没有看到任何允许非线性衰减的系统,您不必在某些时候重新计算分数。问题是,您是否需要在每次投票时都这样做,或者后台 cron 作业可以吗?
  • cron 工作很糟糕。可以,但我有点决心找到一种非持久性流程风格的解决方案。
  • 从不同的角度来看,我通常不喜欢看到其他人建议改变你正在使用的技术,更不用说我自己了,但我会在这里,因为最简单的答案是使用计算列/功能索引,不是 MySQL 提供的。只是在偶然的情况下,它对您很重要,并且您有可能使用其他数据库。
  • 我在 postgres 上。懒惰并且不自己查找东西,但如果你有一个 postgres 解决方案,那就太棒了。

标签: scalability ranking-functions


【解决方案1】:

我也一直在尝试这样做。我找到了一个看起来像解决方案的方法,但不幸的是,我忘记了如何做数学,所以我无法理解它。

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

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

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

【讨论】:

    【解决方案2】:

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

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

    是这样的:

    1. 在每次投票时,将分数推送更新到该投票的下一次衰减到列表的尾部
    2. 然后从列表的顶部弹出第一个投票
    3. 如果它还没有老化到腐烂的程度,就把它推回头部
    4. 否则,从总分中减去所需数量,并将更新的信息推送到尾部
    5. 从第 2 步开始重复,直到获得足够新的投票(第 3 步)

    当然,你仍然需要在后台检查头像以清除没有人投票的帖子。

    【讨论】:

    • 这个想法有一些优点,但它似乎仍然需要一个 cron 工作来进行清理,这是我倾向于尝试的架构决策,并且只使用 10 英尺的柱子。当我遇到这样一个有趣的问题时,我发誓我脑子里的齿轮开始转动得更快了。
    【解决方案3】:

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

    MySQL 的 BIGINT 最大值为 2^64

    为简单起见,我们使用 1 天作为时间间隔。设 n 为网站启动后的天数。

    1. 创建一个整数变量。让我们称它为 X 并从 0 开始
    2. 如果加法运算的分数超过 2^64,首先将每个分数除以 2^n 来更新,然后将 X 设置为 n。
    3. 在每次投票中,得分增加 2^(n-X)。

    所以,从心理上讲,这对我来说使用以 10 为基数更有意义。当我们把这些东西加起来时,我们的数字会变得越来越长。我们不再关心较低数字位置的数字,因为我们增加分数的值有很多数字。这意味着较低的数字停止计数非常多。所以如果他们不计算,为什么不把小数点滑到我们关心的点,并在某个点截断小数点以下的数字。为此,我们还需要在每次添加的金额上滑动小数位。

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

    【讨论】:

    • 首先,当您更新每一行、索引和 X 时,您会做什么?禁用网站? :) 因为否则如果有人在这几分之一秒(或更多?)内投票,那么该帖子将非常幸运。
    • 就我而言,这可能是一个可行的解决方案。更新可以作为事务运行,我的“投票”不会以这样的速度出现,这会成为一个问题。但是,如果能找到一个解决方案,该解决方案涉及为单次投票更新单行并且到此结束。
    • 旧故事获得新投票不会受到处罚。如果你有一个每天都在获得投票的老故事,它会留在头版。
    【解决方案4】:

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

    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
    

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

    如果需要,您也可以在后台作业中运行查询,它们仍然会给出相同的结果。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-11-01
      • 2011-03-16
      • 2012-07-30
      • 2010-10-29
      • 2010-10-26
      • 2014-09-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多