redis 实现搜索热词统计“当日 top10”和“当周 top10”两种方案的抉择

核心问题

一个项目中,遇到了搜索热词统计的需求,我使用了 Redis 的五大数据类型之一 Sorted Set 实现。目前有两项数据需要统计:“当日搜索热词 top10”和“当周搜索热词 top10”。

关于这两项数据的统计方法,目前想到了两种实现方法:

  1. 两个 Redis 的 Sorted Set 实现,一个 Sorted Set A 统计当天,0 点 top10 记录进 MySQL,Sorted Set 清零。一个 Sorted Seted B 统计当周,每周日 top10 记录进 MySQL,Sorted Set B 清零。
  2. 只使用用一个 Sorted Set 记录当天搜索热词,0 点 top10 记录进 MySQL,Sorted Set 清零。到周日时,会有 7 * 10 行记录。把这 7 * 10 行遍历,每次便利都记录进 Sorted Set,全部遍历结束后,再从 Sorted Set 中取出 top10 记录进 MySQL 的周热词统计表中。

Sorted Set 是 Redis 的数据结构,方法 1 会占用两份内存,一份当天的,一份当周的。方法 2 会提高系统的复杂度,并且在统计周表时,可能会出现短时间内大量的计算(当然可以使用定时任务放到凌晨进行)。

请问我该选择哪种解决办法比较好呢?

硬件与用户量

我在写的是一个学生项目,ECS 是 1 核 2GB 内存的学生机(别歧视呀哈哈,穷学生 😅)。如果当天的搜索热词和当周的搜索热词都使用 Redis 内存记录,我怕内存会爆炸。

目前生产环境的 ECS 内存用了 1GB,保守还剩 900MB,我怕 Redis 消耗两份内存会炸。目前是一个小程序的后端,快要上线了,预计初期用户数量不多。关键词长度限制最大 8

天马行空想一下,1MB = 1048576 字节,按两个字节存一个字算,1MB 能存 1048576/2/8 = 65,536 个不重复的搜索关键词,看起来好像可以存很多。我没熟练使用过 Redis,不知道这样天马行空地计算,是否真的可以呢 🤔

问题中涉及的相关知识(如已经了解问题,这部分可忽略)

一个项目中,遇到了搜索热词统计的需求。我使用了 Redis 的五大数据类型之一 Sorted Set 实现。

Redis 有序集合(sorted set)

Redis 有序集合和集合一样也是 string 类型元素的集合,且不允许重复的成员。

不同的是每个元素都会关联一个 double 类型的分数。Redis 正是通过分数来为集合中的成员进行从小到大的排序。

有序集合的成员是唯一的,但分数(score)却可以重复。

集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。 集合中最大的成员数为 232 - 1 (4294967295, 每个集合可存储 40 多亿个成员)。

01sorted set 展示

如上图,Redis 的 Sorted Set 自带排序功能。

操作方法也比较简单,在本项目中,核心是两个方法:

zincrby 命令,对于一个 Sorted Set,存在的就把分数加 x (x 可自行设定),不存在就创建一个分数为 1 的成员。

zrevrange,查询集合中指定顺序的值。返回有序的集合中,score 大的在前面。

  • Redis

    Redis 是一个开源的使用 ANSI C 语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value 数据库,并提供多种语言的 API。从 2010 年 3 月 15 日起,Redis 的开发工作由 VMware 主持。从 2013 年 5 月开始,Redis 的开发由 Pivotal 赞助。

    201 引用 • 237 回帖 • 703 关注
  • 数据库

    据说 99% 的性能瓶颈都在数据库。

    281 引用 • 595 回帖 • 1 关注
  • Q&A

    提问之前请先看《提问的智慧》,好的问题比好的答案更有价值。

    1761 引用 • 11536 回帖 • 581 关注

赞助商 我要投放

被采纳的回答
  • 88250 1 赞同

    嗯,没问题就这样计算,一般的 CRUD 项目不用怎么考虑内存占用,如果真要考虑降低运行时内存换编程语言 doge

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
  • 88250 1 1 赞同

    前者,分开维护清晰明了。

    1 回复
  • 2501224066 1 1 赞同

    当然分开啊

    1 回复
  • JellyfishMIX

    谢谢 D 大,回复好快。我在写的是一个学生项目,ECS 是 1 核 2GB 内存的学生机(别歧视呀哈哈,穷学生 😅)。如果当天的搜索热词和当周的搜索热词都使用 Redis 内存记录,我怕内存会爆炸。

    目前生产环境的 ECS 内存用了 1GB,保守还剩 900MB,我怕 Redis 消耗两份内存会炸。目前是一个小程序的后端,快要上线了,预计初期用户数量不多。关键词长度限制最大 8

    天马行空想一下,1MB = 1048576 字节,按两个字节存一个字算,1MB 能存 1048576/2/8 = 65,536 个不重复的搜索关键词,看起来好像可以存很多。我没熟练使用过 Redis,不知道这样天马行空地计算,是否真的可以呢

    1 回复
  • 88250 1 赞同

    嗯,没问题就这样计算,一般的 CRUD 项目不用怎么考虑内存占用,如果真要考虑降低运行时内存换编程语言 doge

    2 回复
  • JellyfishMIX

    明白了,谢谢

  • JellyfishMIX

    嗯嗯嗯,我决定采用分开,还能少写点代码,哈哈哈。谢谢!

  • JellyfishMIX

    请问我这样的设计,MySQL 的热词统计“周数据”和“天数据”,我也想了两种方案:

    1. 周数据和天数据存在一张 table 中,用一个标志位字段 grade 来判断是周数据还是天数据
    2. 周数据和天数据分成两张表

    上述单表和双表,哪个方案比较好呢

    1 回复
  • 88250

    其他业务列字段都一样的话一张表,但如果周天数据量相差太大就两张表,分开容易扩展或者运维。

    1 回复
  • JellyfishMIX

    明白了,谢谢

  • DASHU

    直接存表吧 我理念中能数据库的就先数据库

    1 回复
  • JellyfishMIX

    考虑过直接存表,不过出于性能的考虑,选择了多使用 Redis 的方案。其实还有更合适的 elastic search,但是我还没有学 😅

  • qloog

    简单点的话 天的 Redis key 用 date[例如:20200603],周的可以用 [年 + 第几周] 就可以搞定

    1 回复
  • JellyfishMIX

    哈哈哈,不错的逻辑。有点类似于分库分表的序号思想。对我来说是不可能用 Redis 存长期数据的,数据在内存待 7 天我就感觉难受,我对内存走火入魔得想方设法节省 😅(当然换编程语言是不可能的doge

    1 回复
  • qloog

    这种排行类的用 Redis 完全没有问题,Redis 也不是完全基于内存,也会定时写到磁盘的。当然如果要想落到数据库的也可以存一份,仅仅是存储,读取还是走 Redis。

请输入回帖内容 ...