首页
题库
公司真题
专项练习
面试题库
在线编程
面试
面试经验
AI 模拟面试
简历
求职
学习
基础学习课
实战项目课
求职辅导课
专栏&文章
竞赛
搜索
我要招人
发布职位
发布职位、邀约牛人
更多企业解决方案
AI面试、笔试、校招、雇品
HR免费试用AI面试
最新面试提效必备
登录
/
注册
求求给个offer我什么都会做的
门头沟学院 后端工程师
发布于上海
关注
已关注
取消关注
写的很好~
学会了
场景题先设计一个最naive的系统,然后根据面试官的引导,一步步地优化
毕竟好的系统都是迭代产生的 过早的优化是万恶之源
@不知名Java选手遗憾转go:
场景题:点赞系统设计
刷到此贴的友友春招/暑期必上岸!!!前言鼠鼠在秋招的过程中多次被问到场景题,中大厂的考察频率相当之高,一般会放在最后一个问题用来拖时间,也遇到过上来就问你怎么设计一个系统(面试官以此来决定后面对你的态度)。所以鼠鼠准备开这个场景题栏目,分享在秋招过程中遇到的场景题以及如何进行回答,感兴趣和感觉有帮助的友友点个关注和赞吧,你们的点赞和关注是鼠鼠持续更新下去的最大动力!!!对于场景题,鼠鼠觉得拿到一道题,首先要思考的是业务逻辑,然后就是在这个业务上会有多大的qps请求量,面试官经常会对你设计的方案和系统提出高并发/大流量的情况下会出现什么问题,你如何去解决,从而考察你设计系统的高可用性和系统性。话不多说开启今天的主题,点赞系统设计吧!!!大家在刷牛客的时候,刷到优质的帖子会点赞加收藏,认为这个博主有不错的产出还会点一个关注(暗示友友们给我点个免费的关注和赞嘛,球球了)那么点赞其实也就成了牛客的核心功能之一,因为每日用户这么多的情况下,点赞所承受的并发其实是相当高的。而且点赞无论是对贴主还是看客,都是比较重要的,涉及到发帖的质量和自身的曝光度。一个大V的帖子,其点赞量,其实对后台的系统的压力,数据库的压力是很具有考验的。在这种情况下应该如何设计点赞系统?那么关于系统设计题,我在上篇文章里已经提到了一些通用的方法论,感兴趣的小伙伴可以去看一看,当然我这里也贴心的贴在这里,帮助大家更好的理解。(1)明晰项目诉求:点赞系统即为存储每一个用户发布的帖子有哪些人点赞了,总的点赞数是多少;每一个用户具体点赞了哪些帖子,赞过的帖子总数是多少(其实这个逻辑友友们点开自己的牛客收藏和点赞其实就一目了然了)整体上是一个多对多的关系。同时关注请求量,是否需要做高并发/高可用的扩展设计;此外对实时性是否也要要求,追求强一致性还是最终一致性(2)确定业务逻辑:对于每个帖子,需要记录点赞的用户,点赞的总人数,取消点赞时点赞数要相应减1,用户查看自己发布的帖子需要显示总点赞数,以及展示一个点赞用户的列表(简单概括为增删改查)。同理,对于观看帖子的用户,需要记录用户点赞了哪些帖子,用户点赞帖子的总数,在我的 - 点赞页面需要展示出用户点赞过的帖子……(3)架构设计:存储(MySQL/Redis)+服务(是否需要拆分为多个服务)友友们会发现,整体的业务逻辑并不是很复杂,但是因为这是面试题,面试官就会问你在高并发的情况下你设计的系统会有什么问题。这其实也是我回答场景题的惯用套路,根据请求量一步一步迭代自己的设计,我们在实际生产过程中也同样是这样一个步骤,先思考最简单的实现,然后一步一步迭代,直到可以抗住高并发,实现高可用(满足面试官的要求[赞])业务逻辑梳理清楚了,接下来我们就来到存储与服务设计了,因为点赞信息是比较重要的,所以这里还是考虑使用MySQL做持久化存储。(做过黑马点评的友友可能直接就会想到Redis做存储,利用incrby 自增命令快速实现点赞功能,这当然是可以的,但是从系统设计和信息丢失的角度来看,先从MySQL入手可以保证数据的安全性,也更能体现面试者的系统设计能力,逐步优化的一个思想)数据表的设计点赞记录表1)type,也就是业务类型,你是为视频点赞还是为评论点赞,还是为其他业务类型点赞2)作者ID3)作品ID4)userID:点赞人ID5)点赞时间6)点赞动作(1 like/ 2 unlike)点赞计数表1)type,也就是业务类型,你是为视频点赞还是为评论点赞,还是为其他业务类型点赞2)作者ID3)作品ID4)点赞数量5)取消点赞数量以上是我的点赞表的设计思路,还有其他的点赞表设计,大家可以根据自己的理解进行修改。在请求量不大的情况下,可以直接用MySQL承载。后端服务会处理点赞的业务逻辑,然后将点赞记录存储到数据库。点赞的业务逻辑1 )前端用户发起点赞请求到达后端服务。后端服务会判断表内是否有之前的点赞记录,因为前端用户可能会重复的去点击点赞按钮。2.1 )如果没有记录就插入点赞记录,之后点赞计数表加一2.2 )如果表内有了用户对作品的点赞记录,就去比较点赞的时间3.1)如果时间小于表内记录的时间,说明当前的请求是之前的请求,丢掉就可以了3.2)如果是大于表内的记录的点赞时间,就继续判断状态4.1)如果已经是点赞状态了,说明就是重复的点赞请求,就丢掉就可以了4.2)如果是取消点赞的状态,就更新记录为点赞状态即可,同时可以增加点赞计数表的数量加一整个点赞的逻辑就处理完了。流程图如下:取消点赞的逻辑1)当前端用户取消点赞,后端服务检查计数表内是否有对应用户对该作品的记录。2.1)如果没有,说明之前没有点赞过,直接丢到返回前端即可。2.2)如果有记录,继续判断时间,如果时间小于数据库内记录时间,说明是之前的请求,直接丢掉就可以了。3.1)如果是大于表内记录时间,说明就继续判断记录的点赞状态。3.2)如果表内是取消点赞的状态,说明是重复请求了,直接丢到就可以了。4)如果表内是点赞状态,更改为取消状态,之后点赞数减一即可。流程图如下:并发问题整个过程是有并发的问题,需要加分布式锁。点赞过程中的并发问题1)重复插入问题:在并发情况下,可能会有多个点赞请求同时到达后端,并且都检查到数据库中没有该用户对该作品的点赞记录,然后都执行插入操作,导致重复插入点赞记录。2)计数不准确问题:当多个点赞请求并发执行时,可能会出现点赞计数表加一的操作不是原子性的情况。例如,两个并发请求同时读取了点赞计数表中的值为 10,然后各自执行加一操作并更新回数据库,最终结果可能是 11 而不是正确的 12。取消点赞过程中的并发问题1)重复减一问题:类似于点赞过程中的计数不准确问题,当多个取消点赞请求并发执行时,如果点赞数减一的操作不是原子性的,可能会导致点赞数被多减。例如,当前点赞数为 5,两个并发的取消点赞请求同时读取点赞数并执行减一操作,最终结果可能是 4 而不是正确的 3。2)状态更新不一致问题:在并发环境下,可能会出现两个请求同时判断点赞状态为点赞,然后一个请求先将其更新为取消状态并减一,另一个请求再更新时可能会出现错误的结果。可以采用数据库事务来确保操作的原子性和一致性,或者使用分布式锁来保证同一时间只有一个请求能执行点赞或取消点赞的操作。同时,在数据库设计上,可以考虑使用乐观锁或悲观锁机制来防止数据冲突。查询点赞列表和点赞数在获取作品点赞数时,通常有两种方式:一种是逐个获取每个作品的点赞数,另一种是通过批量接口一次性获取多个作品的点赞数。逐个获取作品点赞数:如果有多个作品需要获取点赞数,每次都单独向服务器发送请求获取一个作品的点赞数,那么每一次请求都会产生一次网络 IO。例如,要获取 10 个作品的点赞数,就需要进行 10 次网络请求,这会消耗较多的网络资源和时间。使用批量接口获取点赞数:批量接口允许在一次请求中传递多个作品的标识(如作品 ID),服务器接收到请求后,会一次性处理这些作品的点赞数查询,并将结果返回。这样,无论要获取多少个作品的点赞数,都只需要一次网络请求。例如,同样是获取 10 个作品的点赞数,通过批量接口,只需一次网络请求就能完成,相比逐个获取,大大减少了网络 IO 的次数,提高了数据获取的效率,通过批量接口依次获取来减少网络IO的次数,节省了网络带宽和请求时间。引入Redis作为缓存当流量大起来以后,此时数据库的读写锁的竞争就会加剧。考虑加入缓存层Redis。既然引入了缓存,那么面试官肯定会问到一个经典的问题,那就是:缓存和数据库的一致性是如何解决的,这里有多种解决方案,但常用的其实也就是两种:先更新数据库,再删除缓存;通过canle订阅binlog日志,再更新缓存,实现最终一致性。关于缓存和数据库的一致性后面我也会专门出一个专题分享给大家。引入Redis作为缓存后,所有的写请求还是会先写入到数据库,读请求会优先到达缓存层去检查数据,没有再去数据库内加载数据填充到缓存中。Redis中的数据存储Redis会存储两类数据:存储点赞列表和点赞数1)点赞列表:使用zset结构,key由 userid:type 组成,表示的含义即为某个用户执行点赞/评论的作品value就是作品ID+时间戳的set的集合,基于zset可以根据时间戳进行排序注意zset的长度是有限制的,如果无限制的往zset内添加数据就可能会有big key的问题,可能会阻塞Redis的主线程,影响正常的请求。2)点赞数:使用string结构即可。key是作品ID,value是具体的点赞数业务逻辑查询点赞数:先是去Redis查看某个作品的点赞数,如果存在直接返回给客户端,如果不存在则查询数据库计数表内的数据。然后存储到Redis内,最后返回给客户端数量即可。点赞列表查询:首先还是会去Redis内查询点赞列表,如果不存在,去mysql里查询,将数据写回到Redis的zset内即可。这里依然要注意zset长度,最后会返回给客户端就可以了。如果存在就进行zset分页(类似于MySQL的分页查询,不用直接返回所有的数据,而是根据分数,这里是时间戳,查询所需要的页面数据即可)。如果页面超过了zset分页范围,超出的部分就会去数据库内查询,拼接结果返回客户端即可。如果未超过zset长度限制,直接返回当前页的数据给前端就可以了。对于写请求,前端用户发起点赞请求,设计到缓存的更新。这里建议采用canal监听mysql的binlog。根据binlog数据变化,更新缓存内的数据。涉及到两个操作。一个是更新点赞记录,一个是更新点赞数量,由于点赞记录用的是zset处理,并且是有长度限制的。如果超过长度限制,要删除掉多余的元素。整个操作不是原子的,所以用lua脚本来保证整个过程的原子性。没有采用直接删除缓存类的数据因为点赞是一个很频繁的动作,如果频繁的删除数据,其实会频繁的去数据库加载数据,缓存的命中率其实就会很低,实际上并没有给数据库降低压力。架构升级随着业务数据的上升,单机的mysql面临着严重的考验,mysql处理io的能力是有限的,此时考虑到mysql的分库分表来分担单库的磁盘压力,单库的压力。但分库分表其实是会带来系统的复杂性的,还有就是大V数据分布不均匀的问题。当然这里我们只是从系统架构的角度去一步步迭代设计,大家对于分库分表的常见问题也需要有了解,后面我也会出专门的文章来给大家做分享那么不做分库分表的话,运用三板斧之一的消息队列同样也可以通过异步消息来为数据库来消峰填谷。那么这里既然使用到了MQ,那么相关的消息不丢失以及消息消费的幂等性问题也就随之而来了,因为本文主题是点赞系统的设计,这些八股问题我们留到后面的文章中再做解释。当写请求到达时候直接发送到mq,然后返回给前端,点赞成功即可。mq之后就是消费任务,可以订阅点赞的topic进行消费,然后进行点赞的业务逻辑,存入数据库和Redis。新的架构模式下的业务逻辑 1)一个点赞请求过来的时候,消息方法MQ后可以直接返回客户端点赞成功了,后续由MQ异步的处理写数据库和缓存的操作,这里为了保证消息不丢失可以采用同步发送,将消息持久化到磁盘上。2)消费任务监听消息的到达进行点赞写入的业务逻辑。这里的点赞还是和前面的初步设计时是一样的,这里不过多赘述了。3)按照我们之前的架构是先写数据库,然后由canal监听binlog日志的变化,最后更新Redis缓存总结:相信大家看完这篇文章对点赞系统这样一个高并发系统的设计已经有了一个初步的印象了。这里总结概括一下就是:先考虑流量不大的情况,设计出初步的业务逻辑,当流量大起来以后,考虑使用Redis作为缓存,这里考虑缓存和数据库的一致性问题;如果Redis也扛不住了,那就再加上消息队列,进行流量削峰,同时通过异步调用提高客户端的响应速度;最后考虑到数据量庞大的问题,采用分库分表来解决存储和读写效率问题。大家以后遇到高并发系统就可以围绕这样一个大致思路去作答。这里还有网关/限流等概念没有涉及到,点个关注吧,后续的文章会陆续补充欧,希望可以为你的面试带来帮助!PS:其实在整个分析过程中大家可以发现,场景题其实就会把我们背的那些八股和技术运用起来,所以在学习场景题的时候就可以把八股文进行问题,有点像单词背不住就去读阅读文章,在读文章的时候记住八股文,在上面的分析过程中我也有几处进行了随机的八股提问。扫码登录这个过程里MySQL,Redis,分库分表,MQ用的很多,那友友们是不是可以顺带复习一下相关八股呢?……好了如果大家有什么问题的话欢迎来评论区交流。包括但不限于文章创作改正意见,后续分享内容(面经,知识输出,经验分享等等),都看到这了,点个免费的关注和赞不过分吧[好无辜]#大家都开始春招面试了吗##我发现了面试通关密码##暑期实习 ##春招##场景题##八股#
点赞 71
评论 15
全部评论
推荐
最新
楼层
暂无评论,快来抢首评~
相关推荐
12-18 16:51
四川大学 Java
事到如今,头铁就业还是保研
末九本,gpa前25%,有个水赛国二,综合成绩可以本校保研,但之前个人不是很喜欢科研,所以在这学期小厂实习了三个月,方向是java后端。现在是十二月下旬,离职已经过了快半个多月,但是大厂实习一直找不到,官网ssob都石沉大海,唯一约面的还给我前一天临时取消说项目上线不面了。今日剖析审视了下自己,其实家里也没有很急着让我找工作,只是我想早点赚钱。但是经历一个学期的实习和面试之后,我觉得自己很矛盾,一方面想早点赚大钱有一份出人头地的工作,一方面又想求稳选择自己感兴趣的方向享受生活。 现在给我留的就是三条路: 1.继续找大厂实习,风险很大,就算找到了,三月底暑期开面,可能来不及准备 2.沉淀到三月底...
点赞
评论
收藏
分享
12-17 16:30
浙江大学 Java
谈谈 SQL 注入及防范
图解SQL-牛客面经八股
点赞
评论
收藏
分享
11-03 14:32
门头沟学院 Java
找日常实习
想找个日常实习,有无牛友给点意见,没有实习经验,也没有面过一家公司,纯小白。能混进大厂么。还是说沉淀一下冲暑期,但是怕暑期没有实习经验没机会
点赞
评论
收藏
分享
11-05 17:49
中国科学技术大学 算法工程师
那就…只能先就业了?
gpa因为长期实习是依托保研大抵无望…..考研是不会一点公司前景似乎还不错,转正也似乎比较稳,算力薪资都比大厂多点(实习和正式都是),而且不加班5天8.5上班18下班估计读研读博除了多花了时间薪资也很难增长了这方面有没有懂哥简历感觉越写越少….面试也好久没面了…lc也好久没做不会点…我有点菜鼠了
NAce:
哥,少年班戎马归来才多大年纪啊
你的简历改到第几版了
点赞
评论
收藏
分享
12-17 15:48
浙江大学 Java
什么是协程?
图解操作系统-牛客面经八...
点赞
评论
收藏
分享
评论
点赞成功,聊一聊 >
点赞
2
分享
评论
提到的真题
返回内容
全站热榜
更多
1
...
“我希望你把我当成资源,而不是老师。”
5340
2
...
2025年度总结-成长
4029
3
...
2025这一年真是辛苦了
3968
4
...
我从反复确认,走向不再自证
3851
5
...
突然被同事表白了
2796
6
...
双非鼠鼠0实习0竞赛闯秋招的一年,重头再来我不会选这条路了
2756
7
...
数字马力
2682
8
...
百度日常一二面面经已OC
2271
9
...
双非硕后端秋招情况(准备春招版)
2203
10
...
在回河南的火车上总结我的2025
2171
创作者周榜
更多
正在热议
更多
#
实习简历求拷打
#
7841次浏览
96人参与
#
担心入职之后被发现很菜怎么办
#
265217次浏览
1122人参与
#
秋招被挂春招仍然能投的公司
#
5581次浏览
88人参与
#
作业帮求职进展汇总
#
81773次浏览
531人参与
#
什么是优秀的实习经历
#
7403次浏览
197人参与
#
mt对你说过最有启发的一句话
#
32133次浏览
394人参与
#
为了找工作你花了哪些钱?
#
74704次浏览
359人参与
#
考研失败就一定是坏事吗?
#
199780次浏览
1363人参与
#
摸鱼被leader发现了怎么办
#
98458次浏览
629人参与
#
秋招特别不鸣谢
#
14516次浏览
172人参与
#
选实习,你更看重哪方面?
#
12607次浏览
207人参与
#
第一次面试
#
1035801次浏览
13682人参与
#
今年秋招你收到了多少封邮件?
#
17190次浏览
218人参与
#
金融财会交流会
#
128192次浏览
441人参与
#
实习教会我的事
#
46389次浏览
352人参与
#
京东美团大战,你怎么看?
#
157330次浏览
853人参与
#
你今年的保底offer是哪家
#
154759次浏览
671人参与
#
携程求职进展汇总
#
838758次浏览
5510人参与
#
听到哪句话就代表面试稳了or挂了?
#
237306次浏览
1677人参与
#
毕业论文进行时
#
20663次浏览
130人参与
牛客网
牛客网在线编程
牛客网题解
牛客企业服务