【Java】微博系统设计:怎么应对热点事件的突发访问压力?

一、问题解析

微博(microblog)是一种允许用户即时更新简短文本(比如140个字符),并可以公开发布的微型博客形式。今天我们就来开发一个面向全球用户、可以支持10亿级用户体量的微博系统,系统名称为“Weitter”。

我们知道,微博有一个重要特点就是部分明星大V拥有大量的粉丝。如果明星们发布一条比较有话题性的个人花边新闻,比如宣布结婚或者离婚,就会引起粉丝们大量的转发和评论,进而引起更大规模的用户阅读和传播。

这种突发的单一热点事件导致的高并发访问会给系统带来极大的负载压力,处理不当甚至会导致系统崩溃。而这种崩溃又会成为事件热点的一部分,进而引来更多的围观和传播。

因此,Weitter的技术挑战,一方面是微博这样类似的信息流系统架构是如何设计的,另一方面就是如何解决大V们的热点消息产生的突发高并发访问压力,保障系统的可用性。今天我们就来看看这样的系统架构该怎么设计。

13.1 需求分析

Weitter的核心功能只有三个:发微博,关注好友,刷微博。

  1. 发微博:用户可以发表微博,内容包含不超过140个字的文本,可以包含图片和视频。
  2. 关注好友:用户可以关注其他用户。
  3. 刷微博:用户打开自己的微博主页,主页显示用户关注的好友最近发表的微博;用户向下滑动页面(或者点刷新按钮),主页将更新关注好友的最新微博,且最新的微博显示在最上方;主页一次显示20条微博,当用户滑动到主页底部后,继续向上滑动,会按照时间顺序,显示当前页面后续的20条微博。
  4. 此外,用户还可以收藏、转发、评论微博。

13.1. 性能指标估算

系统按10亿用户设计,按20%日活估计,大约有2亿日活用户(DAU),其中每个日活用户每天发表一条微博,并且平均有500个关注者。

而对于发微博所需的存储空间,我们做如下估算。

  • 文本内容存储空间

遵循惯例,每条微博140个字,如果以UTF8编码存储汉字计算,则每条微博需要\(\\small 140\\times3=420\)个字节的存储空间。除了汉字内容以外,每条微博还需要存储微博ID、用户ID、时间戳、经纬度等数据,按80个字节计算。那么每天新发表微博文本内容需要的存储空间为100GB。

\(\\small 2亿 \\times (420B +80B) = 100GB/天\)

  • 多媒体文件存储空间

除了140字文本内容,微博还可以包含图片和视频,按每5条微博包含一张图片,每10条微博包含一个视频估算,每张图片500KB,每个视频2MB,每天还需要60TB的多媒体文件存储空间。

\(\\small 2亿\\div5\\times500KB+2亿\\div10\\times2MB=60TB/天\)

对于刷微博的访问并发量,我们做如下估算。

  • QPS

假设两亿日活用户每天浏览两次微博,每次向上滑动或者进入某个人的主页10次,每次显示20条微博,每天刷新微博次数40亿次,即40亿次微博查询接口调用,平均QPS大约5万。

\(\\small 40亿\\div(24\\times60\\times60)=46296/秒\)

高峰期QPS按平均值2倍计算,所以系统需要满足10万QPS。

  • 网络带宽

10万QPS刷新请求,每次返回微博20条,那么每秒需访问200万条微博。按此前估计,每5条微博包含一张图片,每10条微博包含一个视频,需要的网络总带宽为4.8Tb/s。

\(\\small (200万\\div5\\times500KB+200万\\div10\\times2MB)\\times8bit=4.8Tb/s\)

13.2 概要设计

在需求分析中我们可以看到,Weitter的业务逻辑比较简单,但是并发量数据量都比较大,所以,系统架构的核心就是解决高并发的问题,系统整体部署模型如下。

这里包含了“Get请求”和“Post请求”两条链路,Get请求主要处理刷微博的操作,Post请求主要处理发微博的请求,这两种请求处理也有重合的部分,我们拆分着来看。

我们先来看看Get请求的部分。

用户通过CDN访问Weitter的数据中心、图片以及视频等极耗带宽的请求,绝大部分可以被CDN缓存命中,也就是说,4.8Tb/s的带宽压力,90%以上可以通过CDN消化掉。

没有被CDN命中的请求,一部分是图片和视频请求,其余主要是用户刷新微博请求、查看用户信息请求等,这些请求到达数据中心的反向代理服务器。反向代理服务器检查本地缓存是否有请求需要的内容。如果有,就直接返回;如果没有,对于图片和视频文件,会通过分布式文件存储集群获取相关内容并返回。分布式文件存储集群中的图片和视频是用户发表微博的时候,上传上来的。

对于用户微博内容等请求,如果反向代理服务器没有缓存,就会通过负载均衡服务器到达应用服务器处理。应用服务器首先会从Redis缓存服务器中,检索当前用户关注的好友发表的最新微博,并构建一个结果页面返回。如果Redis中缓存的微博数据量不足,构造不出一个结果页面需要的20条微博,应用服务器会继续从MySQL分片数据库中查找数据。

以上处理流程主要是针对读(http get)请求,那如果是发表微博这样的写(http post)请求呢?我们再来看一下写请求部分的图。

你会看到,客户端不需要通过CDN和反向代理,而是直接通过负载均衡服务器到达应用服务器。应用服务器一方面会将发表的微博写入Redis缓存集群,一方面写入分片数据库中。

在写入数据库的时候,如果直接写数据库,当有高并发的写请求突然到来,可能会导致数据库过载,进而引发系统崩溃。所以,数据库写操作,包括发表微博、关注好友、评论微博等,都写入到消息队列服务器,由消息队列的消费者程序从消息队列中按照一定的速度消费消息,并写入数据库中,保证数据库的负载压力不会突然增加。

13.3 详细设计

用户刷新微博的时候,如何能快速得到自己关注的好友的最新微博列表?10万QPS的并发量如何应对?如何避免数据库负载压力太大以及如何快速响应用户请求?详细设计将基于功能需求和概要设计,主要讨论这些问题。

13.3.1 微博的发表/订阅问题

Weitter用户关注好友后,如何快速得到所有好友的最新发表的微博内容,即发表/订阅问题,是微博的核心业务问题。

一种简单的办法就是“推模式”,即建一张用户订阅表,用户关注的好友发表微博后,立即在用户订阅中为该用户插入一条记录,记录用户id和好友发表的微博id。这样当用户刷新微博的时候,只需要从用户订阅表中按用户id查询所有订阅的微博,然后按时间顺序构建一个列表即可。也就是说,推模式是在用户发微博的时候推送给所有的关注者,如下图,用户发表了微博0,他的所有关注者的订阅表都插入微博0。

推模式实现起来比较简单,但是推模式意味着,如果一个用户有大量的关注者,那么该用户每发表一条微博,就需要在订阅表中为每个关注者插入一条记录。而对于明星用户而言,可能会有几千万的关注者,明星用户发表一条微博,就会导致上千万次的数据库插入操作,直接导致系统崩溃。

所以,对于10亿级用户的微博系统而言,我们需要使用“拉模式”解决发表/订阅问题。也就是说,用户刷新微博的时候,根据其关注的好友列表,查询每个好友近期发表的微博,然后将所有微博按照时间顺序排序后构建一个列表。也就是说,拉模式是在用户刷微博的时候拉取他关注的所有好友的最新微博,如下图:

拉模式极大降低了发表微博时写入数据的负载压力,但是却又急剧增加了刷微博时候读数据库的压力。因为对于用户关注的每个好友,都需要进行一次数据库查询。如果一个用户关注了大量好友,查询压力也是非常巨大的。

所以,首先需要限制用户关注的好友数,在Weitter中,普通用户关注上限是2000人,VIP用户关注上限是5000人。其次,需要尽量减少刷新时查询数据库的次数,也就是说,微博要尽量通过缓存读取。

但即使如此,你会发现每次刷新的查询压力还是太大,所以Weitter最终采用“推拉结合”的模式。也就是说,如果用户当前在线,那么就会使用推模式,系统会在缓存中为其创建一个好友最新发表微博列表,关注的好友如果有新发表微博,就立即将该微博插入列表的头部,当该用户刷新微博的时候,只需要将这个列表返回即可。

如果用户当前不在线,那么系统就会将该列表删除。当用户登录刷新的时候,用拉模式为其重新构建列表。

那么如何确定一个用户是否在线?一方面可以通过用户操作时间间隔来判断,另一方面也可以通过机器学习,预测用户的上线时间,利用系统空闲时间,提前为其构建最新微博列表。

13.3.2 缓存使用策略

通过前面的分析我们已经看到,Weitter是一个典型的高并发读操作的场景。10万QPS刷新请求,每个请求需要返回20条微博,如果全部到数据库中查询的话,数据库的QPS将达到200万,即使是使用分片的分布式数据库,这种压力也依然是无法承受的。所以,我们需要大量使用缓存以改善性能,提高吞吐能力。

但是缓存的空间是有限的,我们必定不能将所有数据都缓存起来。一般缓存使用的是LRU淘汰算法,即当缓存空间不足时,将最近最少使用的缓存数据删除,空出缓存空间存储新数据。

但是LRU算法并不适合微博的场景,因为在拉模式的情况下,当用户刷新微博的时候,我们需要确保其关注的好友最新发表的微博都能展示出来,如果其关注的某个好友较少有其他关注者,那么这个好友发表的微博就很可能会被LRU算法淘汰删除出缓存。对于这种情况,系统就不得不去数据库中进行查询。

而最关键的是,系统并不能知道哪些好友的数据通过读缓存就可以得到全部最新的微博,而哪些好友需要到数据库中查找。因此不得不全部到数据库中查找,这就失去了使用缓存的意义。

基于此,我们在Weitter中使用时间淘汰算法,**也就是将最近一定天数内发布的微博全部缓存起来,用户刷新微博的时候,只需要在缓存中进行查找。如果查找到的微博数满足一次返回的条数(20条),就直接返回给用户;如果缓存中的微博数不足,就再到数据库中查找。

最终,Weitter决定缓存7天内发表的全部微博,需要的缓存空间约700G。缓存的key为用户ID,value为用户最近7天发表的微博ID列表。而微博ID和微博内容分别作为key和value也缓存起来。

此外,对于特别热门的微博内容,比如某个明星的离婚微博,这种针对单个微博内容的高并发访问,由于访问压力都集中一个缓存key上,会给单台Redis服务器造成极大的负载压力。因此,微博还会启用本地缓存模式,即应用服务器在内存中缓存特别热门的微博内容,应用构建微博刷新页的时候,会优先检查微博ID对应的微博内容是否在本地缓存中。

Weitter最后确定的本地缓存策略是:针对拥有100万以上关注者的大V用户,缓存其48小时内发表的全部微博。

现在,我们来看一下Weitter整体的缓存架构。

13.3.3 数据库分片策略

前面我们分析过,Weitter每天新增2亿条微博。也就是说,平均每秒钟需要写入2400条微博,高峰期每秒写入4600条微博。这样的写入压力,对于单机数据库而言是无法承受的。而且,每年新增700亿条微博记录,这也超出了单机数据库的存储能力。因此,Weitter的数据库需要采用分片部署的分布式数据库。分片的规则可以采用用户ID分片或者微博 ID分片。

如果按用户ID(的hash值)分片,那么一个用户发表的全部微博都会保存到一台数据库服务器上。这样做的好处是,当系统需要按用户查找其发表的微博的时候,只需要访问一台服务器就可以完成。

但是这样做也有缺点,对于一个明星大V用户,其数据访问会成热点,进而导致这台服务器负载压力太大。同样地,如果某个用户频繁发表微博,也会导致这台服务器数据增长过快。

要是按微博 ID(的hash值)分片,虽然可以避免上述按用户ID分片的热点聚集问题,但是当查找一个用户的所有微博时,需要访问所有的分片数据库服务器才能得到所需的数据,对数据库服务器集群的整体压力太大。

综合考虑,用户ID分片带来的热点问题,可以通过优化缓存来改善;而某个用户频繁发表微博的问题,可以通过设置每天发表微博数上限(每个用户每天最多发表50条微博)来解决。最终,Weitter采用按用户ID分片的策略。

二、粉丝福利 

  • 我根据我从小白到架构师多年的学习经验整理出来了一份50W字面试解析文档、简历模板、学习路线图、java必看学习书籍 、 需要的小伙伴斯我一下,或者评论区扣“求分享” 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/865099.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

不同系统间数据交换要通过 api 不能直接数据库访问

很多大数据开发提供数据给外部系统直接给表结构,这是不好的方式。在不同系统间进行数据交换时,通过API(应用程序编程接口)而非直接访问数据库是现代系统集成的一种最佳实践。 目录 为什么要通过API进行数据交换如何通过API进行数据…

UG NX二次开发(C#)-根据草图创建拉伸特征(UFun+NXOpen)

文章目录 1、前言2、在UG NX中创建草图,然后创建拉伸特征3、基于UFun函数的实现4、基于NXOpen的实现代码1、前言 UG NX是基于特征的三维建模软件,其中拉伸特征是一个很重要的特征,有读者问如何根据草图创建拉伸特征,我在这篇博客中讲述一下草图创建拉伸特征的UG NX二次开发…

分布式链路追踪Micrometer Tracing和ZipKin基础入门与实践

【1】概述 在分布式与微服务场景下,我们需要解决如下问题: 在大规模分布式与微服务集群下,如何实时观测系统的整体调用链路情况。 在大规模分布式与微服务集群下,如何快速发现并定位到问题。 在大规模分布式与微服务集群下&…

AI网络爬虫006:从当当网批量获取图书信息

文章目录 一、目标二、输入内容三、输出内容一、目标 用户输入一个图书名称,然后程序自动从当当网批量获取图书信息 查看相关元素在源代码中的位置: 二、输入内容 第一步:在deepseek中输入提示词: 你是一个Python爬虫专家,一步步的思考,完成以下网页爬取的Python脚本任…

法制史学习笔记(个人向) Part.3

5. 三国两晋南北朝法律制度 以下为三国魏晋南北朝直到唐代的历史发展脉络图: #mermaid-svg-6AVVMjllKTBaBbRO {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-6AVVMjllKTBaBbRO .error-icon{fill:#55222…

【Python实战因果推断】14_线性回归的不合理效果4

目录 Debiasing Step Denoising Step Standard Error of the Regression Estimator Debiasing Step 回想一下,最初由于混杂偏差,您的数据看起来是这样的、 随着信贷额度的增加,违约率呈下降趋势: 根据 FWL 定理,您可…

c文件读写

格式输入输出 文件指针 FILE *pf 文件打开 fopen("文件名",打开方式)  文件名:普通字符串 打开方式:读、写、文本文件、二进制文件 rt、wt、at、rb、wb、ab、rt、wt、at、rb、wb、ab (r 为读,w 为写, 为读写&…

解锁机器学习潜力的钥匙:深度剖析交叉验证集的应用与魅力

一、为何需要交叉验证集 在构建机器学习模型时,我们通常会面临一个关键问题:如何确保模型在新数据上的表现与在训练数据上一样出色?这涉及到模型的泛化能力——即模型对未见过的数据做出准确预测的能力。传统的训练集/测试集划分方法虽然简单…

亿纬锂能社招入职通用职业能力测评大易题库及薪资待遇

一、亿纬锂能薪资待遇 1. **平均工资**:根据职朋职业圈的数据,惠州亿纬锂能股份有限公司的平均工资为10924元/月。网易新闻的报道则提到,亿纬锂能的月收入平均值为16598元。 2. **工资区间**:在亿纬锂能,工资收入有多…

使用dot来画流程图

Dot是一种图形描述语言,属于Graphviz软件的一部分。Graphviz是一个用于可视化图形(图表、网络图等)的开源工具集。使用Dot语言,你可以创建并描述节点和边,从而生成图形。以下是如何使用Dot语言画图的基本步骤&#xff…

【CSAPP】-attacklab实验

目录 实验目的与要求 实验原理与内容 实验设备与软件环境 实验过程与结果(可贴图) 实验总结 实验目的与要求 1. 强化机器级表示、汇编语言、调试器和逆向工程等方面基础知识,并结合栈帧工作原理实现简单的栈溢出攻击,掌握其基…

游游的水果大礼包(枚举)

题目链接:https://ac.nowcoder.com/acm/problem/255193 题解 题目解析 就拿第一个例子来看,当选择组成1个一号礼包和1个二号礼包时最大的价值是3元,而选择2个二号礼包时,最大的价值是4元,因此选择2个二号礼包。 算法…

2-23 基于matlab的小波变换碰磨故障信号的特征提取

基于matlab的小波变换碰磨故障信号的特征提取,可以画出信号原图,轴心轨迹,频谱图以及多层小波变换的重构信号。程序已调通,可直接运行。 2-23 小波变换 碰磨故障信号 轴心轨迹 - 小红书 (xiaohongshu.com)

html+css+js写的多人在线积分系统

可以添加成员&#xff0c;成员名称自定义 可以对各个成员加分减分➕➖ 可以删除成员 源码在图片下面&#xff0c;记得点赞加关注❤️❤️❤️ 界面 源代码 <!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8">…

2.2.5 C#中显示控件BDPictureBox 的实现----ROI交互续2

2.2.5 C#中显示控件BDPictureBox 的实现----ROI交互续2 1 ROI数组作用说明 变量&#xff1a;m_ROIs[5] ROI 使用效果图 ROI数组说明 2 ROI显示逻辑图 ROI 交互主要是在设定状态下&#xff0c; runmode下只要普通显示即可 3 主要ROI显示函数函数 判断当前鼠标是否获取…

怎么快速给他人分享图片?扫描二维码看图的简单做法

现在通过二维码来查看图片是一种很常见的方法&#xff0c;通过二维码来查看图片不仅能够减少对手机存储空间的占用&#xff0c;而且获取图片变得更加方便快捷&#xff0c;只需要扫码就能够查看图片&#xff0c;有利于图片的展现。很多的场景中都有图片二维码的应用&#xff0c;…

个人微信二次开发

​ 由于自身在机器人方面滚爬多年&#xff0c;现在收藏几个宝藏机器人 推荐一下自己常用的机器人&#xff1a; 适合有技术开发的公司&#xff0c;可以自主开发所需要的功能&#xff01;十分齐全 测试问文档&#xff1a;https://www.wkteam.cn/ 有需要的兄弟可以看一下&#…

音视频同步的关键:深入解析PTS和DTS

&#x1f60e; 作者介绍&#xff1a;我是程序员行者孙&#xff0c;一个热爱分享技术的制能工人。计算机本硕&#xff0c;人工制能研究生。公众号&#xff1a;AI Sun&#xff0c;视频号&#xff1a;AI-行者Sun &#x1f388; 本文专栏&#xff1a;本文收录于《音视频》系列专栏&…

STM32 ADC精度提升方法

STM32 ADC精度提升方法 Fang XS.1452512966qq.com如果有错误&#xff0c;希望被指出&#xff0c;学习技术的路难免会磕磕绊绊量的积累引起质的变化 硬件方法 优化布局布线&#xff0c;尽量减小其他干扰增加电源、Vref去耦电容使用低通滤波器&#xff0c;或加磁珠使用DCDC时尽…

vue+js实现鼠标右键页面时在鼠标位置出现弹窗

首先是弹窗元素 <div class"tanchuang move-win1"id"tanchuang1"><el-button>111</el-button></div>然后在需要弹窗的地方监听点击事件&#xff0c;可以将这个方法写在页面载入事件中 // 获取弹窗元素 var tanchuang document.…