本文始发于个人公众号:TechFlow,原创不易,求个关注
今天是分布式专题的第13篇,今天的文章我们不讲空洞的理论,来聊一个实际点的问题。
众所周知,微博的程序员经常不定期加班。和别的程序员不同,别的岗位的程序员可能加班是可控的,但是微博的程序员不是。为什么呢?因为程序员们无法预知明星们什么时候有新的大料产生,一旦有新料,微博崩溃是妥妥的。甚至很多粉丝用微博有没有崩溃来衡量一个明星的知名度。
这当然只是个段子,但是对于我们搞技术的来说总忍不住问一个问题,微博也算是一个蛮大不小的公司,不缺钱也不缺人,为什么系统这么不稳定呢?
当然我没有在微博呆过,为了严谨起见,我们假设我们现在开了一个类似的新的app,就叫做微特好了。我们来看看微特这个app从0开始成长的过程当中究竟会遇到哪些问题。通过微特这个虚假的项目,我们多少也可以推测一下微博不稳定背后的原因。
微特的诞生
某年某月某天,微特诞生了,为了简化问题,我们假设微特只有两个功能,就是发微特和看微特。
刚刚诞生的小企业嘛,请不起知名的架构师,当然是怎么简单怎么来。我们拆分一下功能,会发现一共只有三个功能,用户发微特、关注其他用户和查看微特,这三个功能都很简单,几乎就是裸的增删改查,我们一个一个看。
先看用户发微特,用户发微特我们只需要用一张表记录微特的内容即可。这个表呢大概长这样:
第二个功能是关注用户,我们再用一张表记录一下用户之间的关注关系即可,这也非常简单:
有了这两张表之后,我们要查看微特就简单了,我们只需要通过userid去关注表里面去关联关注的用户,然后在用关注的用户的id去关联发过的微特即可。
我们用SQL来表示一下这个查询关系。
select wetwi.*
from (
select followeeid
from follower
where followerid = curid
)a join wetwi
on a.followeeid = wetwi.userid
经过一番测试之后,微特顺利上线,老板试用了一下,觉得很满意。新用户也涨得很快,负责的程序员A得到老板的赏识,一路晋升上去变成管理层,不再直接负责项目的技术问题了。
此人的一个手下B被委以重任,负责起了项目,但是就在这个时候,问题出现了。
微特一阶段
随着用户数量的增大,用户们关注的数字开始了迅速增长,很快平均一个用户都关注了好几十个其他用户。每次用户刷新的时候,都会同时加载这好几十人发布的微特内容。加上微特支持图片,最多的时候一时刻需要加载好几十张图片和大量文本,服务器来不及传输这么多数据,频繁的有用户反馈说刷新的时候总是超时。
B想了想,这个简单啊,我们可以分成开源和节流两步。
首先我们需要开源,除了加大机器数量之外,我们需要租借CDN,将用户上传的图片放到CDN上而不存储在数据库里,从而加速数据传输的速度。
节流怎么办呢,也简单我们再用一张表来存储用户最近一次刷新的时间。这样我们在查询的时候,只需要查询这个时间点之后的数据就行了。于是我们多了一张记录时间的表。
这么一搞,系统总算稳定住了,用户抱怨的人数进一步变少。很快用户量又有了爆炸式的提升,终于有一天,热点事件一个接着一个,系统终于扛不住了,接二连三地宕机。
为什么会宕机呢?
因为数据库的查询需要时间,本来就比较慢,加上了join操作之后,系统变得更慢。现在由于要判断微特发送的时间来做过滤,额外增加了一次查询,到达瓶颈之后自然就扛不住了。
B绞尽脑汁,终于想到了办法,就是缓存。他缓存了用户最后一次刷新的时间,这样就可以减少一次数据库的查询。系统稍微变快了一点,但是并没有坚持多久,终于老板忍无可忍开除了B,花大价钱从大公司挖来了一个架构师C。
与此同时,微特的用户量进一步增长,到了第二个阶段。
微特二阶段
C熟悉了环境之后,气得直骂娘,不从架构考虑问题,只会修修补补要不得啊。怎么可以直接用DB存储数据呢,搞成这样不崩溃才有鬼。
新官上任三把火,搞走了几个混吃等死的老白兔,C又招来当年的几个手下,开始大张旗鼓地重构系统。在分布式系统当中,DB往往都是瓶颈,能不用DB的就不应该用DB。而且微特的模式非常明显,用户发布了新内容之后,最关心的就是他的粉丝,那么我们直接在他更新的时候,就把内容推送到粉丝的缓存里,当用户刷新的时候,直接从缓存拉取数据,这不是要方便得多吗?
C画出了新的架构图(可见C也是灵魂画手):
微特直接通过kafka等消息系统推送给用户,效率要比DB高多了,C的一帮手下也给力,很快就完成了架构升级。上线之后,果然效果非凡,系统立刻变得稳定了许多,用户抱怨大幅减少。
在这几年里,公司声名鹊起,积累了大量的资源,一举去美国上市了。公司的高管都财富自由了,C来得晚没分到多少,并且看到当年埋了大坑的A混的风生水起,加上长久以来的不和,气不过跑路了。
就在这风生水起的时候,意想不到的事情又发生了。而我们的微特也来到了阶段3。
微特三阶段
C走了之后,系统从此再也没有大的改动,大家都只想把眼前修修补补的工作做好。反正公司也上市了,老板也自由了,底层的员工只想安安稳稳赚点工资。加上系统也稳定得很,没有人觉得有什么改动的必要。
但是由于微特现在太火了,使用的用户越来越多,除了普通用户之外,还把明星艺人吸引来了。这些明星艺人,一个个都有好几千万的粉丝。不是之前升级了架构,改成了通过kafka推送消息而不是去DB查询吗?这下好了,这些明星每次一发微特,都需要发送好几千万次消息。
有几天不知道是过节还是碰巧,这些明星艺人偏偏一起发推,直接就把发送消息的系统挤压挂了。这一挂,虽然微特整个系统没有崩溃,但是吃瓜群众发现再也搜索不到微特了。于是一起在网上骂微特,这一骂,需要发送的消息更多,系统更加扛不住,彻底成了死循环,公司的口碑急转直下。
公司也想解决这个问题,奈何之前做了太多修修补补的工作,整个系统变得非常复杂,很多模块耦合在了一起,加上也没有一个很好的方案,一直也没有一个人下得了决心整个重构,反而往上加了更多补丁。
直到后来,A离职了之后,公司新来的CTO招来了一个业内技术骨干D。D提出了新的想法,将前面两种方案作了折中。大V发送的时候,并不推送到所有粉丝,而是只推送给当前在线的粉丝。离线的粉丝还是通过查询来获取数据。这样一来对流量进行了分流,微特系统变得稳定了许多。
但是这并没有真正彻底解决问题,系统仍然有时候会宕机,但是一时半会也没有什么特别好的办法,系统也变得极为臃肿彻底经不起折腾。于是微特就这样修修补补,变成了程序员加班不规律的公司。
后续
所谓的微特当然是个段子,故事也不是真的,但是技术架构的变迁还是很有参考意义。一个看似简单的问题,真正落地到应用场景当中,往往没有那么简单。系统眼前的稳定也不能代表长远,这也是我们在开始一个项目的时候就需要眼光长远做好规划的原因。更是一个优秀的架构师的价值所在,只不过可惜的是,世上的道理总是这样,知道的人太多,做到的太少。
希望今天这个的这个故事在博君一笑的同时也能给大家一点启发,一个成功的优秀的系统背后,究竟藏着多少思考和心血呢?
今天的文章就是这些,如果觉得有所收获,请顺手点个关注或者转发吧,你们的举手之劳对我来说很重要。