首页
题库
公司真题
专项练习
面试题库
在线编程
面试
面试经验
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用的很多,那友友们是不是可以顺带复习一下相关八股呢?……好了如果大家有什么问题的话欢迎来评论区交流。包括但不限于文章创作改正意见,后续分享内容(面经,知识输出,经验分享等等),都看到这了,点个免费的关注和赞不过分吧[好无辜]#大家都开始春招面试了吗##我发现了面试通关密码##暑期实习 ##春招##场景题##八股#
点赞 61
评论 13
全部评论
推荐
最新
楼层
暂无评论,快来抢首评~
相关推荐
05-26 17:07
西南民族大学 用户研究员
博士新就业方向出现了!
东南大学招聘膳食管理岗要求博士学位,令人好奇收入如何?最新回应:年薪18万,有厨师证最好。 “虽在岗位任务中提到烹饪,但不意味日常工作是炒菜”。还想问问,这边卡年龄吗
点赞
评论
收藏
分享
05-27 22:19
字节跳动_飞书_前端开发实习生(实习员工)
挑战 26 届最速转正失败——我做错了什么
(黑话警告⚠️:hc=岗位数量, mt=导师, ld=直属领导, cr=代码审查)25年1月,我加入了字节某前端团队,并期望能在这里待到秋招并尝试转正。然而,就在上周,ld 找我1v1,告诉我,我的能力和团队预期不太匹配,并和我劝退。晴天霹雳吗?肯定是有的。那一刻,脑子里嗡嗡作响,各种情绪翻涌。但冷静下来想想,这几个月,自己在能掌控的范围内,确实有不少地方做得不尽如人意。所以,我想把这段不算成功的经历复盘一下,希望能给同样在努力转正的你提个醒,避开我踩过的坑。一、ld 的要求要注意刚进组时,ld就和我聊过转正的事。我当时发问:“咱们这儿有hc 吗?” ld没直接回答,只是说:“看能力,能力到了...
牛客上的彭于晏:
过来人告诉你,入职后要做的第一件事儿不是说主动找活儿做,你要先学会融入团队,摸清ld的性格,投其所好。然后才是展示你的能力,能力上可以说技术或者业务,以业务能力为主,技术能力为辅。优先保证自己对业务需求的开发保证质量效率,然后再谈技术的问题,不要你觉得啥啥啥不行就想着整体优化了(发现校招生最喜欢干这事儿),我工作快5年了发现搞这种的最后都没啥好的结果,产出没有还引入新的bug,校招或者实习的水平看到的问题别人看不到嘛?为什么别人不去搞?浪费时间还没收益的事儿不要去做,技术上的能力体现在对于一个新需求,在不符合现在业务发展的架构设计上,你能拿出好的技术方案同时能考虑到后续业务发展逐渐将技术架构引入合理的架构,这是一个漫长的过程而不是一次性的
牛客在线求职答疑中心
职场捅娄子大赛
点赞
评论
收藏
分享
04-03 19:20
广州航海学院 嵌入式工程师
各位大佬帮我看看有嵌入式为啥没人回🥲
劝退式:
感觉有人回才是不正常的
点赞
评论
收藏
分享
昨天 17:39
中国地质大学 Java
字节跳动二面,面试体验一般
今天分享的是训练营的朋友在字节跳动的面试,新鲜出炉的还热乎着呢。题目都挺简单的,但是他的面试体验不太好,因为面试官问了两个类似的问题,感觉有点不认真。下面是面试的内容:面经详解简单介绍下你的项目介绍下教育平台考试模块的业务.包括题目的读写试卷的生成创建之后,试题如何存储试卷如何分发给考生考生如何提交试卷,你们如何收集试卷结果最后怎么判题以下是对每个问题的回答:前端接口使用restful格式,post与get的区别是什么?功能用途: GET:用于获取资源,通常是从服务器检索数据,比如获取文章列表、用户信息等,不会对服务器上的资源进行修改。POST:主要用于传输实体对象,常用于创建或更新资源,比如...
点赞
评论
收藏
分享
评论
点赞成功,聊一聊 >
点赞
2
分享
评论
提到的真题
返回内容
全站热榜
更多
1
...
挑战 26 届最速转正失败——我做错了什么
3.1W
2
...
我的校园恋爱正式结束了
1.2W
腾讯暑期补捞
热聊中
3
...
25的兄弟们还在战斗吗?
1.0W
4
...
坐我后面的妹妹被拉走了!!
8674
5
...
从后端的角度聊一聊:2728届想要搞开发的同学现在最应该干什么?
8427
6
...
毕业照和春招结束代表着我的学生时代落幕了
7317
7
...
好想加班好想加班,啊啊我的钱(っ╥╯﹏╰╥c)
5901
8
...
双非大一百度timeline
4956
9
...
秋招提前上岸,这个机会真的不错!建议大家冲!
4890
10
...
实习不满意,可以直接拎包就走吗?
4890
创作者周榜
更多
正在热议
更多
#
写给毕业5年后的自己
#
1615次浏览
30人参与
#
上班苦还是上学苦呢?
#
220757次浏览
1308人参与
#
华泰证券Fintech星战营
#
166129次浏览
188人参与
#
职场捅娄子大赛
#
304781次浏览
3064人参与
#
市场营销人求职交流聚集地
#
115795次浏览
1078人参与
#
华为求职进展汇总
#
4631325次浏览
28206人参与
#
如何缓解求职过程中的焦虑?
#
4330次浏览
71人参与
#
如果今天是你的last day,你会怎么度过?
#
20584次浏览
188人参与
#
好好告别我的学生时代
#
22174次浏览
454人参与
#
薪资爆料
#
107668次浏览
1156人参与
#
运营商笔面经互助
#
145214次浏览
1622人参与
#
晒一下我的毕业照
#
25213次浏览
228人参与
#
外包能不能当跳板?
#
31255次浏览
200人参与
#
非技术求职现状
#
424743次浏览
2923人参与
#
简历无回复,你会继续海投还是优化再投?
#
68840次浏览
697人参与
#
你的实习什么时候入职
#
248296次浏览
1889人参与
#
记录实习开销
#
15858次浏览
107人参与
#
我是XXX,请攻击我最薄弱的地方
#
26656次浏览
256人参与
#
宇通求职进展汇总
#
19852次浏览
162人参与
#
运营来爆料
#
43392次浏览
323人参与
#
00后45度躺现状
#
93911次浏览
488人参与
牛客网
牛客企业服务