【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.rhkb.cn/news/362228.html

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

相关文章

2024连云港等保测评机构看这里!

2024连云港等保测评机构看这里! 目前连云港暂未有具有等保资质的机构。因此连云港企业可以就近选择江苏省内等保测评机构,或者在网上寻找合适的机构。 连云港城市简单介绍 连云港——江苏省辖地级市,地处沿海中部,东濒黄海&…

访问网站时IP被屏蔽是什么原因?

在互联网使用中,有时我们可能会遇到访问某个网站时IP地址被屏蔽的情况。IP地址被网站屏蔽是一个相对常见的现象,而导致这种情况的原因多种多样,包括恶意行为、违规访问等。本文将解释IP地址被网站屏蔽的常见原因,同时,…

编译VTK静态库

编译VTK静态库遇到问题 vtkCommonCore-9.3d.lib(vtkSMPToolsAPI.obj) : error LNK2019: unresolved external symbol "public: bool __cdecl vtk::detail::smp::vtkSMPToolsImpl<1>::IsParallelScope(void)" (?IsParallelScope?$vtkSMPToolsImpl$00smpdetai…

JVM专题七:JVM垃圾回收机制

JVM专题六&#xff1a;JVM的内存模型中&#xff0c;我们介绍了JVM内存主要分哪些区域&#xff0c;这些区域分别是干什么的&#xff0c;同时也举了个例子&#xff0c;在运行过程种各个区域数据是怎样流转的。细心的小伙伴可能发现一个问题&#xff0c;在介绍完方法弹栈以后就没有…

【仿真建模-anylogic】Scale解析

Author&#xff1a;赵志乾 Date&#xff1a;2024-06-27 Declaration&#xff1a;All Right Reserved&#xff01;&#xff01;&#xff01; 1. 应用场景 Scale是比例尺&#xff0c;用于长度单位和像素之间的换算&#xff0c;anylogic默认为每个agent生成一个scale&#xff0c;…

Navicat 外网连接 mysql (1、通过SSH方式内网访问 2、对外开放3306端口)

1、通过SSH方式内网访问 直接常规方式使用IP、账号密码连接&#xff0c;失败 SSH方式&#xff1a; 常规 选项卡中&#xff1a;localhost录入数据库账号密码 SSH 选项卡中&#xff1a;勾选使用SSH&#xff0c;输入服务器IP、账号、密码 如果出现该错误&#xff0c;可能是服务器…

主流电商平台API接口(天猫获得淘宝商品详情,获得淘宝app商品详情原数据 ,获得淘口令真实url API,按图搜索淘宝商品(拍立淘) API )

主流电商平台商品接口在电商企业中具有重要应用价值。通过商品接口&#xff0c;电商企业可以实现商品同步功能&#xff1a; 商品信息同步&#xff1a;通过接口可以实时同步主流电商平台上的商品信息&#xff0c;包括商品标题、价格、库存、销量等数据&#xff0c;确保企业在自…

GPU_Gems-物理模型的水模拟

创建一个多网格的平面 void GraphicsWindowBase::RenderPlane() {constexpr int width 150;constexpr int depth 150;constexpr int vertNum width * depth;float length 60.f;if (quadVAO 0){float planeVert[vertNum * 5];float offsetX length / (width - 1.f);float…

【精选】数据治理项目实施(合集)05——解码“数据架构”,数据架构包含哪些内容?

上一篇讲到了数据治理项目的前期调研工作&#xff0c;继数据调研工作完成之后&#xff0c;就要开始关于治理工作的各项方案设计&#xff0c;整体方案设计包括数据架构、元数据、主数据、数据质量、数据安全、指标标签体系、数据生命周期管理和管理评价等内容。这一篇重点讲一下…

聊一聊UDF/UDTF/UDAF是什么,开发要点及如何使用?

背景介绍 UDF来源于Hive&#xff0c;Hive可以允许用户编写自己定义的函数UDF&#xff0c;然后在查询中进行使用。星环Inceptor中的UDF开发规范与Hive相同&#xff0c;目前有3种UDF&#xff1a; A. UDF--以单个数据行为参数&#xff0c;输出单个数据行&#xff1b; UDF&#…

为什么说展厅数字人是展览未来的趋势?

展厅数字人是利用数字化、智能化和网络化等信息技术手段提升展厅展览服务和游览体验的全新载体。随着人工智能和虚拟现实技术的应用发展&#xff0c;展厅数字人已成为展厅展览转型升级的重要趋势。 展厅数字人凭借其创新性、强可塑性&#xff0c;成为展厅新名片&#xff0c;为各…

趣测系统搭建APP源码开发,娱乐丰富生活的选择!

文章目录 前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结 前言 趣测系统提供了一个集合多种有趣测试的平台&#xff0c;如心理测试和星座测试等&#xff0c;这些测试内容富有趣味性和娱乐性&#xff0c;能够帮助大众在忙碌的生活中找到放松和娱乐的时刻…

Vite 动态导入警告问题解决方案

如上图我要实现从后台获取权限菜单并动态导入进行渲染 但由于 vite 暂时不支持这种导入方式 图中也给出了提示 本人也是这么去做了 但并没什么卵用 后来参考了 vite 的 import.meta.glob 这种方式 我在处理菜单权限控制的菜单里进行了如下操作&#xff1a; …

Hyperf 在 NginxProxyManager 如何配置 websocket?

新建代理 填写域名等服务信息&#xff0c;选择支持WebSockets。 创建 SSL 编写nginx配置 location /message.io{proxy_pass http://<你的ip>:<对应端口号>;proxy_http_version 1.1;proxy_set_header Upgrade $http_upgrade;proxy_set_header Connection "Upg…

会计报表分析

目录 一. 会计报表的种类 \quad 一. 会计报表的种类 \quad 反应财务状况的是资产负债表 反应经营成果的是利润表 有时间点的就是静态表 动态表就是有一个区间的, 比如一年, 一个季度等

学习笔记——动态路由——RIP(RIP路由汇总介绍)

四、RIP路由汇总介绍 当网络中路由器的路由条目非常多时&#xff0c;可以通过路由汇总&#xff08;又称路由汇聚或路由聚合&#xff09;来减少路由条目数&#xff0c;加快路由收敛时间和增强网络稳定性。 路由汇总的原理是&#xff0c;同一个自然网段内的不同子网的路由在向外…

Django 如何使用视图动态输出 CSV 以及 PDF

Django 如何使用视图动态输出 CSV 以及 PDF 这一篇我们需要用到 python 的 csv 和 reportLab 库&#xff0c;通过django视图来定义输出我们需要的 csv 或者 pdf 文件。 csv文件 打开我们的视图文件 testsite/members/views.py 。新增一个视图方法&#xff1a; import csv …

活用变量,让Postman的使用飞起来

在 Postman 中使用变量是一种非常强大的功能&#xff0c;它可以极大地增强 API 测试和开发的灵活性和效率。 Postman变量的类型 变量在 Postman 中可以在多个层次设置和使用&#xff0c;包括 全局变量环境变量集合变量局部变量&#xff08;如在脚本中暂时创建的变量&#xf…

Clickhouse 的性能优化实践总结

文章目录 前言性能优化的原则数据结构优化内存优化磁盘优化网络优化CPU优化查询优化数据迁移优化 前言 ClickHouse是一个性能很强的OLAP数据库&#xff0c;性能强是建立在专业运维之上的&#xff0c;需要专业运维人员依据不同的业务需求对ClickHouse进行有针对性的优化。同一批…

想问一下stm32学习哪些东西才算入门并且能做项目?

STM32&#xff08;所有的MCU都一样&#xff09;归根结底只是一个工具&#xff0c;能做的事情也很多&#xff0c;如果只谈性能&#xff0c;不考虑稳定性等因素&#xff0c;那么103估计做个导弹控制器&#xff0c;火箭控制器都没有问题&#xff0c;阿波罗登月的主控主频才多少&am…