榜单模型(一):需求分析与方案设计

266 阅读3分钟

一、需求

假定我们现在有一个业务需求:展示一个热点榜单,展示五十条(下图是我在稀土掘金首页上展示的榜单)

image.png

从非功能性上来说,热榜功能通常是作为首页的一部分,或者至少是一个高频访问的页面,因此性能和可用性都要非常高。

问题关键点:

  • 什么样的才算是热点?【产品经理会告诉你】
  • 如何计算热点?【算法层面的,产品经理也会告诉你】
  • 热点必然带来高并发,那么怎么保证性能?【高性能】
  • 如果热点功能崩溃了,怎么样降低对整个系统的影响?【高可用】

二、一些热点模型

那对于第一个问题:什么样的才算是热点?

不同公司的计算方式都不太一样,但是都有一些基本规律。

  • 综合考虑了用户的各种行为:例如观看数量、点赞、收藏等。
  • 综合考虑时间的衰减特性:包括内容本身的发布时间,用户点赞、收藏的时间。
  • 权重因子:这一类可以认为是网站有意识地控制某些内容是否是热点,它可能有好几个参数,也可能是只有 一个综合的参数。(取决于比如当前网络热点事件、国家政策等)

2.1 Hacknews模型

image.png

其中 P 是投票数(或者得票数),T 是发表以来的时间 (以小时为单位)。总体可以认为:得票数最重要,而后热度随着时间衰减

2.2 Reddit模型

image.png

从根本上来说,Reddit 的模型考虑的核心因素就是赞成票、反对票,以及发帖时间

2.3 微博模型

微博热搜榜是通过综合计算微博上的阅读量、讨论量、转发量等数据指标,以及话题或事件的参与人数、参与次数、互动量等数据指标,得出每个话题或事件的实时热度,并按照热度进行排序呈现的。只是给了宽泛的介绍,并没有公开具体的热榜计算算法。

❗本文将采用 Hacknews 的热点模型,P = 点赞数,G = 1.5,T = 已发表时间

三、异步定时计算热榜

3.1 实时计算的痛点

实时计算热榜的难点:全表扫描 + 全局排序

  • 全表扫描:找出所有帖子的点赞数发表时间
  • 计算每个帖子的score并全局排序
  • 找出top50

image.png

3.2 异步定时计算

解决方案:每隔一段时间就计算一次热榜

在这个基础上,要进一步考虑:

1)怎么设计缓存,保证有极好的查询性能。【高性能】

2)怎么保证可用性,保证在任何情况下(即使 mysqlredis 全崩了)都能拿到热榜数据。【高可用】

如何实现一个定时器?

(1)利用 time.Ticker 实现(简单,只能实现固定时间间隔执行不能定时定点执行

func TestTicker(t *testing.T) {
    ticker := time.NewTicker(time.Second)
    defer ticker.Stop()

    ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
    defer cancel()

    for {
       select {
       case <-ctx.Done(): // 超时的情况
          // select 中不要使用 break
          return

       case now := <-ticker.C: // note ticker.C是只读的 channel,每 1s 会产生一个 Time 类型的数据
          log.Println("现在是:", now.UnixMilli())
       }
    }
}

(2)利用 cron 表达式及其开源库实现(既能实现固定时间间隔,也能实现定时定点执行

func TestCronExpr(t *testing.T) {
    expr := cron.New(cron.WithSeconds())

    // 填写 cron 表达式
    id, err := expr.AddFunc("@every 1s", func() {
       t.Log("执行了")
    })
    assert.NoError(t, err)
    t.Log("任务", id)

    // 开始调度任务
    expr.Start()
    time.Sleep(time.Second * 10)

    ctx := expr.Stop() // 意思是,你不要调度新任务执行了,你正在执行的继续执行
    t.Log("发出来停止信号")

    <-ctx.Done()
    t.Log("彻底停下来了,没有任务在执行")
    // 这边,彻底停下来了
}

注意:

  1. 我们使用 github.com/robfig/cron/v3 这个库
  2. 参考这个文档来写 cron 表达式:help.aliyun.com/zh/ecs/user…
  3. 我们使用的 cron 开源仓库提供了一些便捷的写法,如下图

image.png

如何具体实现热榜算法?

(下文再见~)