随着社交网络的蓬勃发展,点赞功能逐渐成为了一个网站中不可或缺的功能。因为点赞功能不仅可以让用户更直观地了解自己的视频、文章等内容被多少人认可,而且也提升了用户互动体验感。下面我们来聊聊通用的点赞系统设计的方案。
1、点赞系统的数据表设计
在设计数据表的时候我们需要知道点赞系统需要完成的基础功能有哪些,点赞系统通常需要实现以下功能:
(1)用户可以点赞一个视频、文章、评论等内容
(2)用户可以查看一个等内容的点赞数
(3)用户可以取消对等内容的点赞
针对如上所示的功能,我们可以设计一张点赞记录表和点赞计数表来记录数据,如下是两张表的字段设计:
点赞计数表中记录了稿件()被点赞和取消点赞的总数,用作总的点赞数据展示;点赞记录表用于记录哪些用户在何时给哪个稿件点赞或取消点赞。
2、系统设计
2.1 点赞数据写入的设计方案
点赞系统一般流量是比较大的,特别是在某个稿件突然成为热点之后,那么流量就会突增上来,为了应对大流量,我们在设计点赞系统的时候采用MQ来做削峰处理,整个点赞数据写入的流程如下所示:
(1)用户发送来点赞请求,经过Nginx和网关转发到点赞服务上,点赞服务组装必要的数据(稿件的id、稿件用户id等数据)发送MQ消息,并发响应客户端写入点赞数据成功。
(2)点赞服务消费MQ消息,首先要保存点赞的数据,在保存点赞数据的时候需要做一些逻辑检验工作,如下的流程图所示:
首先根据用户的id和点赞的稿件id查询数据库获取用户的点赞记录数据,根据查询的结果分如下的情况分析:
(a)如果没有查询到用户的点赞记录数据,那么直接保存用户的点赞记录到记录表中,将点赞计数表中的总的点赞数量加1。
(b)如果已经存在了用户的点赞记录,那么就需要根据点赞的时间和点赞的动作进一步的检查
(b1)数据表中的点赞时间 > MQ中用户的点赞时间,说明可能存在重复的点赞,此时我们这表MQ消息直接丢弃。
(b2) 数据表中的点赞时间 <MQ中用户的点赞时间,比较数据库中当前的用户点赞状态是否为点赞,如果是点赞状态那么当前的MQ也不消费了,如果是数据库中状态是取消状态,那么MQ消息我们就需要消费,此时修改记录表的数据状态为点赞状态、点赞计数表中当前的稿件的点赞数量加1。
(3)点赞的数据写入缓存中从而减轻数据库的压力,点赞记录和点赞计数表的设计如下所示:
在redis中稿件的点赞总数可以采用String类型的数据结构来缓存,点赞记录数据采用Zset的数据格式来存缓存数据,这里需要给redis设置适当的过期时间。
(4)数据库的设计采用读写分离的架构,使用canal来同步数据到从库中,所有的写请求都打到主库上,所有的读请求都转发到从库上。
(5)为了保证数据的一致性,我们采用定时任务定期从数据库中同步数据到redis上,这样即使是redis在某个时间中写失败了,我们通过定时任务的方式将数据补偿到redis中。
取消的点赞数据的写入流程也是一样设计的,只是最终逻辑是要点赞计数表中点赞的数量减1,取消点赞的数量加1的,还要在点赞记录表中更新或者添加用户取消点赞的记录,所以取消点赞的这里就不在赘述。
2.2 用户读取点赞的数据
当点赞数据成功的写入缓存和数据库之后,用户读取点赞数据的流程如下:
(1)读请求转发到点赞服务上之后,点赞服务优先查询redis中是否存在数据,如果有数据的情况下,直接响应数据给客户端。
(2)如果redis中无点赞数据,那么此时就需要到数据库中查询数据,此时读取数据库的时候需要添加锁,防止短时间内由于缓存失效等原因造成大量的请求直接请求数据库从而导致数据库崩溃的问题。数据库上查询的数据要缓存一份到redis中。
总结:
(1)点赞系统本文介绍的是一种通过MQ+主从架构+redis的设计方案来应对大流量
(2)高并发下点赞系统的redis缓存推荐使用更新的方案,因为高并发如果频繁的删除缓存就会导致缓存的命中率下降,那么就发挥不了缓存的作用
(3)主从架构中,主从的通过是通过canal来同步的,canal也是依赖与主库的binlog,如果主库由于系统的压力较大生成binlog速度慢了,就可能会发生从库和redis之间的数据不一致性,此时定时任务可以做数据补偿,修复从库和redis之间的数据不一致性。