当前位置: 首页 > 科技观察

程序员经典面试题:如何实现一个海量的计数系统

时间:2023-03-20 18:37:43 科技观察

我们在开发的时候经常会有一些计数相关的工作。比如抖音上的短视频有评论数和播放数。发微博时,还有转发数、评论数、点赞数等统计。在电子商务网站上,有商品销售量、商品销售数量等统计数据,那么,这些统计数据是如何实现的呢?不难想象,当用户查看这条评论时,我们可以统计点赞总数。比如你在虎扑上发表评论,每次有人点赞,评论下面就会加一条点赞记录,记录点赞过的人。当有人访问这条评论时,我们可以使用Mysql的SelectCount语句对其进行统计,查询一共有多少条记录,返回给上一节。这个方案有什么问题?当数据量不大的时候,我们总能很快的计算出结果。但是随着数据量的增加,比如一个很火的评论可能已经有几十万点赞了,每次查询的效率就比较低了。评论内容是核心数据,评论点赞数是次要数据。如果查询点赞数的时间过长,反而会影响主营业务。我们要做的就是尽量少查询,或者让查询更快。在之前的解决方案中,我们每次都是全额统计。那么,我们可以保存这些数据吗?这样我们就不用每次有人来查询都统计了。每次有变化,就去数据库执行selectCount,查询后保存结果。之后每次有人访问这条评论,我们都会直接返回,不需要SelectCount。当然,每次都去SelectCount也是很笨的做法。为什么我们不在每次有人喜欢时加1,而在每次有人取消喜欢时减1。我们将对类似数据的调用分为两个事件,一个是+1,另一个是-1。当然,这种做法的潜在风险是并发问题。比如原来的数据是5,突然来了两个+1请求。因为它们是并发执行的,所以最后的结果就变成了6。这种情况下,我们就需要加锁来防止并发,或者让数据库替我们做。仅此一点是远远不够的。突然堵车怎么办?比如某热点事件突然爆发,一条评论一下子火了起来,10分钟之内可能就会有上万的点赞。如果我们每次都更新数据库,效率会大大降低。为了应对这种突如其来的流量,业内的解决方案大家都非常熟悉。为什么不使用消息队列来调峰。但是这种突如其来的流量很容易造成MQ堆积,请问有什么好的办法吗?其实很简单。本来我们要进行10个+1操作,为什么不合并成一个+10操作呢?这样可以大大减轻数据库的压力。我们一般有两种常见的实现方式,一种是在异步队列上合并任务,另一种是将计数保存在缓存中,并定期将数据刷入数据库。我们多采用第二种方式,我们会进一步优化,即将点赞的统计数据存储在Redis等缓存中间件中,然后周期性的刷入磁盘。这样的统计数据,忽冷忽热是非常明显的。基本上,采用该方案已经可以适用于大多数计数系统。欢迎大家关注我,一起学习,一起进步。您的支持是我继续聊天的动力。