技术 2018.10.14 42 阅读

《Redis实战》笔记1—用redis实现投票功能

如何利用redis实现一个类似reddit的投票列表,并根据发布时间减少每次投票对其分值的影响

· · ·

  作为一个后台程序员,我想大部分人应该都做过投票这个功能。如果只是针对一次活动投票的话这样的功能实现起来非常简单,只需要将这个列表存入一个有序集合,投票时使用Zincrby来添加相应的票数就可以了。但是像reddit、Stack Overflow这样的网站,可以对帖子进行投票,如果再使用之前的计算方法可能就会出现一个问题。投票数越高的可能就会一直展示在前面并且投票会越来越高,新的帖子可能会永远展示在后面无人问津(就像是某个年代的D8一样)。所以我们现在要做的是用另外一种方式来计算每个值的权重,时间越长对其影响越小,并且实现如何用redis进行存储。

 

我们以文章列表为例,具体的计算方法为:将文章得到的支持票数量乘以一个常量,然后加上文章的发布时间,得出的结果就是文章的评分。计算评分时与支持票数量相乘的常量为432,这个常量是通过将一天的秒数(86 400)除以文章展示一天所需要支持票数量(200)得出的:文章每获得一张支持票,程序就需要将文章的评分增加432分。这样随着时间的推移,时间越长分值也就会相对更低,影响更小    1:文章展示一天所需要支持票数量可根据实际情况而定,此处暂定200。

 

  除了存储评分之外,可能还需要使用Redis存储更多有用的信息,比如文章详情。可以通过使用key值为article:<id>的哈希结构存储 (这样的key结构更易读同时也更易为管理)。PHP的实现方法及其简单,如果您不需要对数据库查询出来的数据做任何修改,可以直接通过hmset命令存储,使用hgetall全部取出,此处本文不再做详细介绍。

  言归正传,我们的投票功能将使用两个有序集合来有序的存储文章:第一个有序集合的成员为文章ID,分值为文章的发布时间;第二个有序集合的成员同样为文章ID,而分值则为文章的评分。通过这两个有序集合,网站既可以根据根据时间顺序来展示文章,也可以根据评分的高低来展示文章。我们还需要一个集合用来存储已投过票的用户ID,防止用户重复投票。同时为了尽量节约内存,文章发布后一周后将不允许投票,可以为集合设置Expire过期时间,而判断文章是否能投票可以用程序逻辑来控制。当用户进行投票时,计算完所需要的分数,再使用Zincrby命令修改相对应的有序集合里ID的分值即可。

  如果文章需要分类,可以将分类和文章做一个映射,存在集合里,有序集合里存储文章ID。Redis可以对多个集合或者有序集合作为输入进行操作,所以我们可以使用Zinterstore命令里几种不同的方式来合并集合和有序集合并最终生成我们所需要的文章列表(所有集合成员的分值会被视为1),但是如果集合里的ID过多执行Zinterstore可能需要花费一定的时间。

 

以上只是简单计算分值的方法,更多的分值计算方法可以参考阮一峰老师的博客