00-系统设计核心
00-系统设计核心
学习核心
系统设计核心思路:
- 掌握系统设计的核心框架(系统设计四部曲:CAPS)
- 对系统设计回答思路有一定的认识
系统演进思路:
- 从 0 到 百万用户的演进思考(对系统演进所涉及的技术要有一定认识,思考用户量和业务扩张的系统扩展方向)
- 16张图图解 | 淘宝十年架构变迁(理解架构演进的过程)
学习资料
- 系统设计面试:内幕指南(中文版本)
- 系统设计面试:内幕指南(第2章:粗略估算)
- 系统设计面试:内幕指南(第3章:系统设计面试框架)
- 系统设计面试:内幕指南(第1章:从0到百万用户)
- 16 张图图解 | 淘宝十年架构变迁!
系统设计思路
1.CAPS
CAPS 整体架构:

- 沟通对齐(Communicate)
- 需求对齐:整体诉求对齐,是做什么,业务流程是怎样,核心功能点要设计啥
- 请求量对齐:QPS(1k,10k,100k的做法是不一样的),不同量级下大方向是什么
- 精准度对齐:精准性要求(能不能多卖、能不能少卖)
- 难点分析(要点分析):结合当前系统背景分析一波难点要点
- 架构设计(Architecture Design):服务+存储+流程
- 服务设计:有几个模块,或者说服务
- 存储设计:用什么选型,为什么
- 流程设计:结合业务串一遍流程,输出整体架构
- 要点展开(Point Design):每个系统难点要点都有区别,但可以从共通点切入,例如核心流程细节、高并发、高精准等
- 例如秒杀场景下:怎么应对高并发?如何解决少卖、多卖的问题?
- 陈述总结(Summarize)
- 总结架构
- 要点总结
① 沟通对齐(Communicate)
不要一上来就哼哧哼哧设计,很明显会暴露自己没有一点设计经验(一般工作三年以上要求逐步有独立设计系统的能力),沟通对齐可以更有目标和方向地进行系统设计,这个过程中还可体现沟通和表达能力
- 需求对齐:在短时间内很难设计一个面面俱到的系统,因此要在有限的时间内沟通好核心的功能点和业务流程
- 例如秒杀场景中:最重要的功能是库存扣减,而不是订单系统
- 请求量对齐:不同请求量的方案设计可谓天差地别,实际工作中不能低估请求量,需要适当高估留点buffer,但也不能大炮打蚊子
- 例如设计一个1000/s的秒杀系统,整个10台Redis集群就不合适了
- 更专业一点来说对于并发量也是需要考虑的,但是通常来说基于QPS设计打底,并发量考虑补充即可
- 精准度对齐:每个场景都有一些数据精准或者数据一致的要求,精度要求不同设计方案也有区别
- 例如缓存一致性场景:允许的不一致时间有多长?
- 例如秒杀场景:针对秒杀性能设计,可否小概率超卖?可否小概率少卖?
- 难点分析(要点分析):可以主动提出哪些点是比较具有挑战性的,需要深入设计的,可以适当提出或者听取对方意见
- 例如秒杀场景:打击黄牛也是一个重要的部分,但是后台设计的核心应该关注库存扣减和高并发怎么扛,如果系统设计跑偏了很难拉回来
② 架构设计(Architecture Design)
对齐了需求,就要进行架构设计,架构设计是大方向的把控,在系统设计中占比超过30%。而针对架构概念的切入不需要死记硬背,只需要从关键核心点切入,然后将他们串联起来,其核心为服务+存储+流程,例如有什么服务?存储选型?结合业务流程如何将这些服务和存储串联起来?以秒杀设计场景为例分析如下
① 服务设计

② 存储设计:针对秒杀场景的高性能和高精准要求,高性能需要Redis、高精准需要MySQL,因此存储选择这两个组件
③ 流程设计:以一个业务流程为例,将这个过程串联起来,构成一个架构设计(重点关注整体架构的大方向,不要纠结于细节,细节部分的调整可以放在后面展开)

③ 要点展开(Point Design)
沟通对齐阶段分析了要点/难点,架构设计阶段设计了系统的整体架构,但对于一个庞大的系统而言,不可能在短短的十几分钟就将所有概念带过,脱离实际这样去搞也不现实。因此在这一阶段要对之前提到的难点、要点部分进行详细分析
每个系统难点要点都有区别,但也有规律。无非是核心流程细节、高并发、高精准等。
比如秒杀,难点无非是高并发和高精准。因此需要去展开高并发的解决方案,去对比,去决策。高精准的话需要考虑到超卖、少卖问题,以及各种异常情况下,是否有机制能兜底。
④ 陈述总结(Summarize)
收尾部分,总结一下就是给出一个深刻一点的结论:用要点作为线索进行讲述,总结关键信息,然后做个收尾工作
说点深刻一点的结论 不同量级和要求下的架构也是不一样的,此处主要还是按比较常见的场景(几十万qps)来做的剖析。在实战中,需要按需设计,原则上不能低估秒杀请求量,但也不要用大炮打蚊子
要点作为线索牵引
在秒杀系统中,我认为最大的挑战是高并发和高精准。解决高并发思路是通过Redis的高性能对名额预处理;解决高精准的思路主要是通过强一致的MVSQL避免超卖,通过定时任务对比库存和名额,解决少卖。除此之外,也要考虑到了黄牛打击、限频兜底,用户体验等问题
收尾
以上就是对秒杀系统的设计,有不成熟的地方还请指教
2.粗略估算
参考文章:系统设计面试:内幕指南(第2章:粗略估算)
在系统设计中,有时会要求你使用粗略估算来估计系统的容量或性能需求。根据谷歌高级研究员杰夫·迪恩(Jeff Dean)的说法,“粗略估算是使用一系列思维实验和常见性能数据的组合进行估算,以便对哪种设计能够满足你的要求有一个良好的了解”
要有效地进行粗略估算,需要对可扩展性基础知识有很好的了解。应该深入理解相关概念:二的幂、每个程序员都应该知道的延迟数据和可用性数据
常见的粗略估算问题包括:QPS、峰值QPS、存储、缓存、服务器数量等,结合实际业务场景去分析计算
① 2的幂次方
尽管在处理分布式系统时数据量可能变得非常庞大,但所有计算归结为基础知识。为了获得正确的计算结果,了解使用二的幂的数据量单位至关重要。一个字节是8个位的序列。一个 ASCII 字符使用一个字节的内存(8位)
Byte、KB、MB、GB、TB、PB、EB、ZB、YB、DB、NB,进率是按照210(1024)进行计算的
名字 | 缩写 | 次方 |
---|---|---|
Kilobyte | KB | 1024(210) |
Megabyte | MB | 1024 KB(220) |
Gigabyte | GB | 1024 MB(230) |
Terabyte | TB | 1024 GB(240) |
Petabyte | PB | 1024 TB(250) |
② 延迟数据
Google的Dean博士在2010年透露了典型计算机操作的时间。 随着计算机变得更快更强大,一些数字已经过时。通过这些数字可以了解不同计算机操作的速度和慢速
操作 | 延迟(纳秒) | 延迟(微秒) | 延迟(毫秒) | 参考 |
CPU L1 级缓存访问 | 0.5 ns | |||
分支预测错误* | 5 ns | |||
CPU L2 级缓存访问 | 7 ns | 14x L1 cache | ||
互斥体Mutex 加锁/解锁 | 25 ns | |||
内存访问 | 100 ns | 20x L2 cache, 200x L1 cache | ||
用Zippy压缩1K字节 | 3,000 ns | 3 us | ||
在1 Gbps速率的网络上发送1K字节 over | 10,000 ns | 10 us | ||
从SSD读取4K长度的随机数据 | 150,000 ns | 150 us | ~1GB/sec SSD | |
从内存读取连续的1 MB长度数据 | 250,000 ns | 250 us | ||
在同一数据中心内的来回通讯延迟* | 500,000 ns | 500 us | ||
从SSD读取连续的1 MB长度数据 | 1,000,000 ns | 1,000 us | 1 ms | ~1GB/sec SSD, 4X memory |
磁盘寻址 | 10,000,000 ns | 10,000 us | 10 ms | 20x datacenter roundtrip |
从磁盘读取连续的1 MB长度数据 | 20,000,000 ns | 20,000 us | 20 ms | 80x memory, 20X SSD |
发送数据包 California->Netherlands->California | 150,000,000 ns | 150,000 us | 150 ms |
ns = 纳秒, μs = 微秒, ms = 毫秒
1 ns=10−9 秒
1 μs=10−6 秒=1,000 ns
1 ms=10−3 秒=1,000 μs=1,000,000 ns
一位谷歌软件工程师构建了可视化工具来可视化Dean博士的数据。该工具还考虑了时间因素,通过测试数据得出如下结论:
- 内存速度快,但磁盘速度慢
- 如果可能的话,应避免磁盘寻道
- 简单的压缩算法速度快
- 在发送数据到互联网之前,尽可能对数据进行压缩
- 数据中心通常位于不同的区域,发送数据之间需要一定的时间
③ 可用性数据
高可用性是系统持续运行的能力,期望能够长时间保持操作。 高可用性通常以百分比表示,100%意味着服务没有任何停机时间。大多数服务的可用性介于99%到100%之间。
**服务水平协议(SLA)**是服务提供商常用的术语。这是你(服务提供商)与你的客户之间的协议,该协议正式定义了你的服务将提供的运行时间水平。 云服务提供商Amazon、Google和Microsoft将它们的SLA设置在99.9%或更高。系统的运行时间传统上以数字的形式进行测量。 数字越多,表示系统的运行时间越长。 如表2-3所示,数字数量与预期系统停机时间相关
可用率 % | 每日停机时间(downtime per day) | 每年停机时间(downtime per year) |
---|---|---|
99% | 14.40 min | 3.65 days |
99.9% | 1.44 minutes | 8.77 hours |
99.99% | 8.64 seconds | 52.60 minutes |
99.999% | 864.00 ms | 5.26 minutes |
99.9999% | 86.40 ms | 31.56 seconds |
举例分析:估算Twitter的查询量和存储需求
假设
- 每月活跃用户为3亿
- 50%的用户每天使用 Twitter
- 用户平均每天发布2条推文
- 10%的推文包含媒体
- 数据存储时间为5年
估算:每秒查询次数(QPS)估计:
- 每日活跃用户(DAU)= 3亿 * 50% = 1.5亿
- 推文 QPS = 1.5亿 * 2推文 / 24小时 / 3600秒 = 约 3500
- 峰值QPS = 2 * QPS = 约 7000
估算存储
- 平均推文大小:
- 推文ID 64 bytes(字节)
- 文本 140 bytes(字节)
- 媒体 1MB
- 媒体存储:1.5亿 * 2 * 10% * 1MB = 每天30TB
- 5年媒体存储:30TB * 365 * 5 = 约 55PB
- 平均推文大小:
3.系统设计框架
参考文章:系统设计面试:内幕指南(第3章:系统设计面试框架)
四步流程分析:
- ① 了解问题并确定设计范围(需求分析)
- 适当提出问题,快速收集建立一个系统所需的信息
- 例如开发什么特定功能?
- 产品面向用户规模?
- 预计业务扩张速度?(3个月、6个月、1年的预期规模)
- 技术栈选型:可否利用现有技术服务支持简化设计?
- 适当提出问题,快速收集建立一个系统所需的信息
- ② 提出高层次的设计方案并对齐(架构设计)
- 初步构思一个设计蓝图,根据版图征询反馈意见
- 勾勒关键组件框架(服务、存储、流程:例如包括客户端、API、网络服务器、数据存储、缓存、CDN、消息队列等)
- 做粗略计算,评估蓝图是否符合规模限制
- ③ 深入设计(针对初步的版图深入设计)
- 优先考虑现有架构设计的组件,结合对方的关注点进行细节补充
- 时间管理尤为重要,学会话术引导,不要被一些细枝末节给迷惑,不要陷入不必要的细节
- ④ 总结
- 抓住系统要点进行总结
- 与对方确认是否有需要补充的问题和扩展,或者现有系统设计瓶颈以及相应的解决方案
设计参考
该做
- 对齐需求:不要自证假设,而是要了解问题的要求
- 尊重差异性:开放式问题既没有正确的答案,也没有最好的答案。为解决年轻创业公司的问题而设计的解决方案与拥有数百万用户的老牌公司的解决方案不同
- 多沟通,不要陷入一人之境:与对方及时沟通,明确自己的思考方向,如有偏差及时转舵(向面试官反映想法,一个好的面试官会把你当作一个团队伙伴和你一起合作)
- 提出多种解决方案:如果可能的话,针对一些场景可以适当提出多种方法
- 关注核心:当设计方向达成一致,就对每个组件进行详细说明,先设计最关键的部分,不要舍本逐末
- 最重要的:永不言弃。
不该做
- 不打无准备之仗:不要对典型的面试问题没有任何准备
- 对齐需求:在没有弄清需求和假设的情况下,不要贸然提出解决方案
- 关注核心:在开始的时候,不要对一个单一的组件进行太多细节的研究。首先给出高层次的设计,然后再深入探讨
- 请求支援:如果你被卡住了,不要犹豫,请求提示
- 及时沟通:再次强调,要进行沟通,不要默默思考。
- 不要自己敲钟:不要认为一旦给出设计方案,整个过程就结束了。直到对方说你完成了,你才算完成,尽早并经常要求反馈。
每个步骤的时间分配
系统设计的面试问题通常非常广泛,45分钟或一个小时不足以涵盖整个设计。时间管理至关重要。在每个步骤上应该花费多少时间? 以下是一个非常粗略的指南,指导你在45分钟的面试会议中的时间分配。请记住,这只是一个粗略的估计,实际时间分配取决于问题的范围和面试官的要求。
- 第1步 理解问题并确定设计范围:3-10分钟
- 第2步 提出高层次的设计并获得认同:10-15分钟
- 第3步 深入设计:10-25分钟
- 第4步 总结:3-5分钟
从 0 到 百万用户的系统演进
1.从0到百万用户
设计一个支持数百万用户的系统具有挑战性,是一个需要持续完善和不断改进的过程。此处通过构建一个支持单个用户的系统,并逐渐扩展以服务数百万用户的系统,在这个过程中掌握一些技巧,以助力解决系统设计的常见问题
① 单服务器设置
单服务器:将所有的东西都运行在一个单独的服务器上,结合图示理解(web应用程序、数据库、缓存)
请求流程分析:
- ① 用户通过域名访问网站(例如
api.mysite.com
),经由DNS(域名系统,一般是第三方提供的付费服务,而不是自己的服务器托管)解析返回真正的IP(例如15.125.23.214
)给到客户端(浏览器或者移动应用程序) - ② 客户端获取到真正的IP地址,则会向Web服务器发送HTTP(超文本传输协议)请求
- ③ 随后Web服务器接收请求并处理响应(返回渲染的HTML页面数据或者JSON格式数据等响应信息)
结合上述请求流程分析,Web服务器的流量来自两个来源:Web应用程序和移动应用程序:
Web应用程序:它使用一组服务器端语言(Java、Python等)来处理业务逻辑、存储等,以及客户端语言(HTML和JavaScript)进行呈现
移动应用程序:HTTP协议是移动应用程序和Web服务器之间的通信协议。由于其简单性,JavaScript Object Notation(JSON)是常用的API响应格式用于数据传输。以下是JSON格式的API响应示例:
GET /users/12 – Retrieve user object for id = 12 { "id": 12, "firstName": "John", "lastName": "Smith", "address": { "streetAddress": "21 2nd Street", "city": "New York", "state": "NY", "postalCode":10021 }, "phoneNumbers": [ "212 555-1234", "646 555-4567" ] }
⚽ V1-单服务器版本
② 数据库
数据库:将数据库独立部署到另一个服务器上,让Web服务和数据库分开部署并允许其独立扩展
随着用户基数的增长,单个服务器已经不够,需要多个服务器:一个用于处理Web/移动流量,另一个用于数据库(参考图示)。将Web/移动流量(Web层)和数据库(数据层)服务器分开允许它们独立扩展(也就是说将Web应用服务
和数据库
分别独立部署到不同的服务器上)

(1)数据库选型
数据库选型:可以在传统关系型数据库和非关系型数据库中进行选择
- 关系型数据库(关系数据库管理系统(RDBMS) / SQL数据库),例如MySQL、Oracle数据库、PostgreSQL等。关系型数据库使用表格和行来表示和存储数据,可以使用SQL在不同的数据库表之间执行连接操作
- 非关系型数据库(NoSQL数据库),例如CouchDB、Neo4j、Cassandra、HBase、Amazon DynamoDB等。这些数据库分为四类:键值存储、图存储、列存储和文档存储,非关系型数据库通常不支持连接操作
对于数据库的选型则需结合实际业务场景需求来择选,对于大部分开发场景来说关系型数据库足以,但是对于特定的数据处理场景有时候非关系型数据库具备更胜一筹的优势。例如在一些特定场景下,可以考虑优选非关系型数据库:
- 应用程序需要超低延迟
- 数据是非结构化的,或者没有任何关系型数据
- 只需要序列化和反序列化数据(JSON、XML、YAML等)
- 需要存储大量的数据
(2)服务扩展(垂直扩展 VS 水平扩展)
此处的服务扩展并不局限于Web服务扩展或者数据库服务扩展,而是针对服务节点概念的性能扩展
- 垂直扩展 VS 水平扩展
- 垂直扩展,又称为“纵向扩展”,指的是通过增加单个服务器的计算能力(CPU、RAM等)来提升其性能
- 水平扩展,又称为“横向扩展”,允许通过向资源池中添加更多服务器来进行扩展
当流量较低时,垂直扩展是一个很好的选择,而垂直扩展的主要优势在于其简单性,但它也带有一些严重的限制:
- ① 无法无限制加配:垂直扩展有一个硬性限制,不可能为单个服务器无限制的添加CPU和内存
- ② 单机设计无故障转移和冗余机制:垂直扩展没有故障转移和冗余,如果一台服务出现宕机,网站和应用程序都会彻底宕机
由于垂直扩展的局限性,对于大规模应用程序来说,水平扩展更为理想:在以前的设计中,用户直接连接到web服务器,如果服务器下线,用户将不能访问网站。在另一种情况下,如果许多用户同时访问web服务器,并且它达到了web服务器的限制,则用户通常会遇到响应较慢或者无法连接到服务器的情况,而负载均衡是解决这些问题的最佳技术方案
⚽V2-数据库独立部署
③ 负载均衡
切入方向:引入负载均衡,支撑服务水平扩展机制
负载均衡器均匀地分配传入的流量到在负载均衡集中定义的Web服务器

流程分析:用户直接连接负载均衡的公网IP,通过此配置,客户端不再直接访问web服务器,为了提高安全性,服务器之间的通信使用私有IP。私有IP是仅在同一网络中的服务器之间可达的IP地址。然而,它在互联网上是不可访问的。负载均衡器通过私有IP与Web服务器通信
基于上述图示分析,在添加了一个负载均衡器和一个扩展服务之后,成功解决了故障切换问题,并提高了web层的可用性:
- 如果服务器1下线,所有流量将被路由到服务器2。这可以防止网站离线,还将在服务器池中添加一个新的健康Web服务器来平衡负载
- 如果网站流量迅速增长,并且两台服务器不足以处理流量,那么负载均衡可以很好的处理这个问题,只需要向web服务器池中添加更多的服务器,负载均衡会自动发送请求给他们
⚽ V3-引入负载均衡
④ 数据库复制
数据库复制可适用于许多数据库管理系统,通常在原始数据库(master)与副本数据库(slaves)之间建立主/从关系
常见的主从架构指的是"一主一从",主数据库通常仅支持写的操作,从数据库从主数据库中同步数据并仅支持度操作。大多数应用程序对读写比的要求较高,因此,系统中从库的数量通常大于主库的数量,进而演变成"一主多从"架构
数据库复制的优势包括:
- 更好的性能:在这个主/从模型中,所有的写入和更新都发生在主节点,而所有的读操作分布在从节点。这种模型提高了性能,因为它允许并行处理更多的查询
- 可靠性:如果其中一台数据库被台风、地震等自然灾害破坏,数据仍然会被保留。此时无需担心数据丢失,因为数据被复制到多个位置
- 高可用性:通过在不同位置复制数据,即使一台数据库离线,网站仍然可以运行,因为可以访问存储在另一个数据库服务器中的数据
如果其中一个数据库离线怎么办?
可以参考上述多机WebServer
部署的架构,通过部署多个数据库来支持故障转移,那么此处就会涉及到不同的数据库部署架构,基于这个架构设计理念可以处理这种情况:
- 如果只有一个从数据库可用且它离线,读操作将临时指向主数据库。一旦发现问题,一个新的从库将会替换掉旧的从库,如果有多个从数据库是可用的,读操作会被转发到其他健康的从数据库
- 如果主数据库离线,一个从库会被提升为新的主库,所有的数据库操作都会临时在新的主库上执行。一个新的从库将会立即替换旧的从库进行数据复制。在生产系统中,提升一个新的主数据库更为复杂,因为从库中的数据可能不是最新的,丢失的数据需要通过运行数据恢复脚本来更新。尽管一些其他的复制方法,如多主复制和循环复制可能有所帮助,但他们的配置更加复杂
在上述基础上添加数据库复制后的系统设计图示分析如下:

流程分析:
- ① 用户从DNS中获取负载均衡器的IP地址
- ② 通过这个IP地址连接到负载均衡器,然后HTTP经由转发到Server1或Server2
- ③ Web服务器根据用户请求操作相关的数据库:
- 如果是读请求:从从数据库中读取
- 如果是写请求:操作主数据库
⚽ V4-引入数据库复制(数据库架构优化)
⑤ 缓存
切入方向:基于上述分析,初步完成web层和数据库层的设计,此时可以将关注点进一步转向提升负载和响应时间(可通过添加缓存层并将静态(JavaScript/CSS/图像/视频文件)移至内容分发网络(CDN)来完成)
缓存是一个临时存储区域,用于将昂贵的响应结果或频繁的访问数据存储在内存中,以便之后的请求能被更快的处理。如下图所示,每当加载新的网页时,会执行一个或多个数据库调用来获取数据。通过重复调用数据库,应用程序的性能会受到很大影响。通过引入缓存可以缓解这个问题
在接收到请求后,Web服务器首先检查缓存是否有可用的响应。如果有,它将数据发送回客户端。如果没有,它会查询数据库,保存响应结果到缓存中,并将其发送回客户端。这种缓存策略称为读取穿透缓存。根据数据类型、大小和访问模式,还有其他可用的缓存策略
使用缓存的注意事项
- ① 决定何时使用缓存:当数据频繁读取但不经常修改时,可考虑使用缓存
- 由于缓存数据存储在易失的内存中,所以缓存服务器不适合持久化数据。例如,如果缓存服务器重启了,内存中所有的数据都会丢失,因此重要的数据应该保存在持久数据存储中
- ② 过期策略:一旦缓存数据过期,它就会从缓存中删除。当没有过期策略时,缓存数据将被永久的保存在内存中
- 缓存过期时间一般不要设置得太短或者太长
- 不要将过期时间设置的太短,因为这会导致系统过于频繁地从数据库重新加载数据
- 不要将过期时间设置的太长,因为数据可能会过时(可能出现数据不一致情况)
- 缓存过期时间一般不要设置得太短或者太长
- ③ 一致性:保持数据存储和缓存同步。一致性可能会发生,因为对数据存储和缓存的修改操作不在一个事务中。在跨多个区域扩展时,保持数据存储和缓存之间的一致性具有挑战性
- ④ 减少故障:单个缓存服务器代表了潜在的单点故障(SPOF:单点故障(SPOF)是系统的一部分,如果它发生故障,将使整个系统停止工作)
- 【方法1】建议在不同数据中心使用多个缓存服务器,以避免单点故障(SPOF)
- 【方法2】通过配置比所需的大小还多一定百分比的内存,这在内存使用量上升的时候起到一个缓冲的效果
- ⑤ 驱逐策略(淘汰策略):一旦缓存满了,任何尝试向缓存中添加内容的请求都可能导致现有项被移除,这称为缓存驱逐
- 最近最少使用(LRU)是最流行的缓存驱逐策略。可以使用其他逐出策略,例如:最不常用(LFU)或先进先出(FIFO),以满足不同的使用场景
⑥ CDN(内容分发网络)
CDN(内容分发网络)是一个由地理上分散的服务器组成的网络,用于提供静态内容。CDN服务器缓存静态内容,如:图片、视频、CSS、JavaScript文件等。
动态内容缓存是一个相对较新的概念,它支持缓存基于请求路径、查询字符串、Cookie和请求头缓存HTML页面。此处终点介绍如何使用CDN缓存静态内容:
在高层次上,CDN的工作原理如下:当用户访问一个网站时,距离用户最近的CDN服务器将提供静态内容。直观的说,用户距离CDN服务器越远,网站加载速度就越慢。例如,如果CDN服务器位于旧金山,那么洛杉矶的用户将比欧洲的用户更快的获取内容
CDN 工作流程分析
① 用户A尝试通过URL访问图片,但是这个URL域名是由CDN提供商提供
② 如果此时CDN服务器缓存中没有这个图片信息,那么CDN服务器会从源(可能是Web服务器或者一些在线存储(例如Amazon S3))请求文件
③ 源返回image.png给CDN服务器,并包含可选的HTTP头部Time-to-Live(TTL),它表示图像被缓存的时间有多长
④ CDN缓存图像并将其返回给用户A。在TTL过期之前,图像一直被缓存在CDN中
⑤ 此时用户B发送一个请求获取相同的图片
⑥ 只要TTL尚未过期,图像就会从CDN缓存中返回
使用CDN缓存需要考虑的因素
- 接入成本:CDN由第三方提供商运行,需要为进出CDN的数据传输付费,对于很少使用的缓存资源,提供不了显著的好处
- 设置适当的缓存过期时间:对于时间敏感的内容,设置缓存过期时间是很重要的,缓存过期时间既不应该太长,也不应该太短。如果时间太长,内容可能不再新鲜,如果时间太短,可能导致反复从源服务器重新加载内容到CDN。
- CDN回源:应该考虑网站/应用程序如何处理CDN故障,如果出现临时CDN中断,客户端应该能够检测问题并从源请求资源
- 使文件无效:在缓存过期之前,可以通过执行以下操作之一从CDN中删除文件:
- 通过CDN服务商提供的API使CDN对象失效
- 使用对象版本控制来提供不同版本的对象。对对象进行版本控制,可以向URL中添加参数例如版本号,例如:在查询字符串中添加版本号2:image.png?v=2
接入CDN和缓存后的设计架构更新如下

引入CDN静态资源缓存后的变化:
- Web服务器不再提供静态资产(JS、CSS、图像等),它们从CDN获取以获得更好的性能
- 通过缓存数据,减轻了数据库的负载
⚽接入缓存和CDN
CDN:静态资源缓存(对接第三方运营商)、Cache:数据缓存(与Server交互,缓存频繁访问的一些数据)
⑦ 无状态的Web层
切入方向:继续优化,考虑水平扩展Web层,为此则需要将状态(例如用户会话数据)移出Web层。一个好的做法是将会话数据存储在持久性存储中,比如关系型数据库或NoSQL。集群中的每个Web服务器都可以从数据库中访问状态数据,这被称为无状态的Web层
(1)有状态架构 VS 无状态架构
有状态服务和无状态服务有一些关键区别。有状态服务器会在一个请求到下一个请求时记住客户端数据(状态)。无状态服务不会保留任何状态信息。
有状态架构现存问题
用户A的会话数据和头像数据存储在Server 1中,要对用户A进行身份验证,必须将HTTP请求路由到Server 1,如果将请求发送到其他服务器,比如Server,身份验证将失败,因为Server 2不包含用户A的会话数据。同样,用户B的所有Http请求都必须路由到Server 2,所有来自用户C的请求必须发送到Server 3
问题在于,来自同一客户端的每个请求必须路由到同一台服务器。在大多数负载均衡器中,可以使用粘性会话(sticky sessions)来实现这一点;然而,这会增加开销,使用这种方法更难添加或删除服务器,处理服务器故障也是一个挑战
无状态架构切入
在无状态架构中,来自用户的HTTP请求可以被发送到任何Web服务器,并从共享的数据存储中获取状态数据。状态数据存储在共享的数据存储中,并且不存储在Web服务器中,一个无状态的系统更简单、更健壮且可扩展
(2)引入无状态架构
引入无状态架构后的架构更新图示如下:

将会话数据从Web层移出,并将其存储在持久数据存储中。共享的数据存储可以是关系型数据库,Memcached/Redis, NoSQL等。选择NoSQL数据存储是因为它易于扩展。自动扩展意味着根据流量负载自动添加或删除Web服务器。在将状态数据移出Web服务器后,可以根据流量负载添加或删除服务器,从而轻松实现Web层的自动扩展。
⚽ V6-无状态架构(接入NOSQL)
⑧ 数据中心
切入方向:当网站发展迅速,并吸引了大量国际用户,为了提高可用性并在更广泛的地理区域提供更好的用户体验,支持多个数据中心至关重要
结合图示分析,左图显示具有两个数据中心的示例设置。在正常运行时,用户通过geoDNS路由(也称为地理路由)到最近的数据中心,美国东部的流量为x%,美国西部的流量为(100-x)%。geoDNS是一种DNS服务,允许根据用户的位置将域名解析为IP地址。在发生任何重大数据中心中断的情况下,将所有流量引导到一个健康的数据中心。结合右图观察,数据中心2(美西)处于离线状态,100%的流量路由到数据中心1(美东)
要实现多数据中心配置,必须要解决几个技术难题:
流量重定向:需要有效的工具将流量重定向到正确的数据中心。geoDNS可以根据用户位置将流量引导至用户最近的数据中心。
数据同步:来自不同地区的用户可以使用不同的本地数据库或缓存,在故障转移的情况下,流量可能被路由到不可用的数据中心,一种常见的策略是跨多个数据中心复制数据
测试和部署:对于多数据中心设置,在不同位置测试网站/应用程序非常重要,自动化部署工具对于保持所有数据中心的服务一致性至关重要
⚽V7-接入数据中心
数据中心
多数据中心
⑨ 消息队列
切入方向:为了更进一步扩展我们的系统,需要解耦系统的不同组件,使它们可以独立扩展。消息队列是许多实际分布式系统采用的关键策略,用于解决这个问题。
消息队列是一个持久性组件,存储在内存中,支持异步通信,它充当缓冲区并分发异步请求。消息队列的基础架构非常简单,输入服务,被称为生产者/发布者,创建消息,并将它们发送到消息队列中。其他服务或服务器,称为消费者/订阅者,连接到队列,并执行消息定义的动作
解耦使消息队列成为构建可伸缩且可靠的应用程序的首选架构。使用消息队列,生产者可以在消费者无法处理消息时将消息发布到队列中。即使生产者不可用,消费者也可以从队列中获取数据。
举例分析
应用程序支持照片自定义,包括剪裁、锐化、模糊等。这些定制任务需要一些时间才能完成。结合图示分析,Web服务器将照片处理作业发布到消息队列。照片处理工作者从消息队列中获取作业并异步执行照片定制任务。生产者和消费者可以独立扩展,当队列的大小变得很大时,添加更多的工作者以减少处理时间。但是,如果队列大部分时间为空,则可以减少工作者的数量。
⚽ V8-接入消息队列
⑩ 日志记录、指标、自动化
切入方向:当处理运行在少量服务器上的小型网站时,日志记录、指标、和自动化支持是很好的选择,但并不是必须的。但是,现在你的网站已经发展成为一个大型业务,那么投资这些工具是必不可少的
日志记录:监控错误日志有助于识别系统中的错误和问题。可以在每个服务器级别监视错误日志,或使用工具将它们汇总到一个集中的服务中,以便于搜索和查看
指标:收集不同类型的指标有助于获得业务洞察能力和了解系统的健康状态。以下一些指标是有用的:
主机级别的指标:CPU、内存、磁盘I/O等
聚合级别的指标:例如,整个数据库层、缓存层的性能
关键业务指标:每日活跃用户、留存数、收入等
自动化:当系统变得庞大而复杂时,需要构建或利用自动化工具来提高生产力。持续集成是一种很好的做法,其中每次代码提交都通过自动化进行验证,使团队能够及时发现问题。此外,将构建、测试、部署过程等自动化,可以显著提高开发人员的生产力
【添加消息队列和不同的工具】更新后的设计(限于篇幅有限,图中仅显示了一个数据中心)
- 该设计包含一个消息队列,有助于使系统更松耦合和故障恢复能力
- 日志记录、监控、指标、自动化工具也包括其中
⚽ V9-接入工具集
⑪ 数据库扩展
==切入方向:==随着数据日益增长,数据库的负载越来越重,因此需要考虑扩展数据层。此处有两种广泛的数据库扩展方法:垂直扩展和水平扩展
垂直扩展(向上扩展):向现有机器添加更多的资源(CPU、内存、磁盘等)进行扩展
- 不足:
- 机器扩展存在上限:单台机器资源无法无上限的扩展
- 存在单点故障问题:无法支持故障转移,一旦某台机器宕机将会不可用,就会导致业务阻塞
- 垂直扩展成本较高:垂直扩展的总体成本更高,强大的服务器要贵得多
- 不足:
水平扩展(水平分片):加机器(添加更多的服务器)
分片将大型数据库分成更小、更易于管理的部分,称为分片。每个分片共享相同的模式,尽管每个分片上的实际数据对该分片来说是独一无二的。
下图展示分片数据库的示例。用户数据根据用户ID分配到数据库服务器。每次访问数据时,都会使用哈希函数来找到相应的分片(此处分片策略为哈希分片)。在此处示例中,user_id % 4
被用作hash函数。如果结果为0,则使用分片0来存储和提取数据;如果结果为1,则使用分片1。其他分片采用相同的逻辑
分片键的选择是实施分片策略时要考虑的重要因素。分片键(也称为分区键)由一个或多个列组成,决定数据的分布方式。结合上图所示,user_id 是分片键,通过分片键将数据库查询路由到正确的数据库来高效地检索和修改数据。在选择分片键时,最重要的一个指标是选择一个可以均匀分布数据的键。
分片是扩展数据库的一项伟大技术,但它远不是一个完美的解决方案。它为系统引入了复杂性和新的挑战:
- 重新分片数据:在以下情况,需要重新分片数据:
- 由于快速增长,单个分片无法再容纳更多数据
- 由于数据分布不均匀,某些分片可能比其他分片更快被耗尽,当分片耗尽时,需要更新分片功能并移动数据
- 名人问题(热点键问题):对一个特定分片的过度访问可能会导致服务器过载
- 想象一下 Katy Perry、Justin Bieber 和 Lady Gaga 的数据最终都在同一个分片上,对于社交应用来说,这个分片将会被读操作淹没。为了解决这个问题,可以为每一个名人分配一个分片,每个分片甚至可能需要进一步分区
- 连接和去范式化:一旦数据库跨多个服务分片,就很难跨数据库分片执行连接操作。常见的解决方法是对数据库进行去范式化,以便可以在单个表中执行查询。
⚽ V10-数据库扩展(引入数据分片)
⑫百万用户及以上
扩展系统是一个持续迭代的过程,为了超越百万用户,需要更多的微调和新的策略。例如,可能需要优化你的系统,将系统解耦为更小的服务。基于上述系统设计思路方向,理解如何扩展系统以支持数百万用户:
- 保持Web层无状态
- 在每一层建立冗余
- 尽可能缓存数据
- 支持多个数据中心
- 在CDN中托管静态数据
- 通过分片扩展数据层
- 将层拆分为单独的服务
- 监控你的系统并使用自动化工具
⑪⑫⑬⑭⑮⑯⑰⑱
2.淘宝十年架构变迁(16张图图解)
参考学习资料:16张图图解 | 淘宝十年架构变迁
学习核心
- 单机架构
- 第一次演进:Tomcat与数据库分开部署
- 第二次演进:引入本地缓存和分布式缓存
- 第三次演进:引入反向代理实现负载均衡
- 第四次演进:数据库读写分离
- 第五次演进:数据库按业务分库
- 第六次演进:把大表拆分为小表
- 第七次演进:使用LVS或F5来使多个Nginx负载均衡
- 第八次演进:通过DNS轮询实现机房间的负载均衡
- 第九次演进:引入NoSQL数据库和搜索引擎等技术
- 第十次演进:大应用拆分为小应用
- 第十一次演进:复用的功能抽离成微服务
- 第十二次演进:引入企业服务总线ESB屏蔽服务接口的访问差异
- 第十三次演进:引入容器化技术实现运行环境隔离与动态服务管理
- 第十四次演进:以云平台承载系统
演进方向思路分析:
- 对于现有架构会遇到什么性能瓶颈,对应的解决方案是什么?
- 当架构演进之后,部分模块能力增强会带来什么新的性能瓶颈?
- 循次往复,不断优化架构设计
① 基础概念
- 分布式系统中的多个模块在不同服务器上部署,即可称为分布式系统,如Tomcat和数据库分别部署在不同的服务器上,或两个相同功能的Tomcat分别部署在不同服务器上
- 高可用系统中部分节点失效时,其他节点能够接替它继续提供服务,则可认为系统具有高可用性
- 集群一个特定领域的软件部署在多台服务器上并作为一个整体提供一类服务,这个整体称为集群。如Zookeeper中的Master和Slave分别部署在多台服务器上,共同组成一个整体提供集中配置服务。在常见的集群中,客户端往往能够连接任意一个节点获得服务,并且当集群中一个节点掉线时,其他节点往往能够自动的接替它继续提供服务,这时候说明集群具有高可用性
- 负载均衡请求发送到系统时,通过某些方式把请求均匀分发到多个节点上,使系统中每个节点能够均匀的处理请求负载,则可认为系统是负载均衡的
- 正向代理和反向代理系统内部要访问外部网络时,统一通过一个代理服务器把请求转发出去,在外部网络看来就是代理服务器发起的访问,此时代理服务器实现的是正向代理;当外部请求进入系统时,代理服务器把该请求转发到系统中的某台服务器上,对外部请求来说,与之交互的只有代理服务器,此时代理服务器实现的是反向代理。简单来说,正向代理是代理服务器代替系统内部来访问外部网络的过程,反向代理是外部请求访问系统时通过代理服务器转发到内部服务器的过程
② 架构演进设计
(1)单机架构
以淘宝作为例子。在网站最初时,应用数量与用户数都较少,可以把 Tomcat 和数据库部署在同一台服务器上。浏览器往 www.taobao.com 发起请求时,首先经过 DNS 服务器(域名系统)把域名转换为实际 IP 地址 10.102.4.1,浏览器转而访问该 IP 对应的 Tomcat
(2)第①次演进:Tomcat与数据库分开部署
问题切入:随着用户数的增长,Tomcat和数据库之间竞争资源,单机性能不足以支撑业务
系统演进:Tomcat和数据库分别独占服务器资源,显著提高两者各自性能
(3)第②次演进:引入本地缓存和分布式缓存
问题切入:随着用户数的增长,并发读写数据库成为瓶颈
系统演进:在Tomcat同服务器上或同JVM中增加本地缓存,并在外部增加分布式缓存,缓存热门商品信息或热门商品的html页面等。通过缓存能把绝大多数请求在读写数据库前拦截掉,大大降低数据库压力。
其中涉及的技术包括:使用memcached作为本地缓存,使用Redis作为分布式缓存,还会涉及缓存一致性、缓存穿透/击穿、缓存雪崩、热点数据集中失效等问题
(4)第③次演进:引入反向代理实现负载均衡
问题切入:缓存抗住了大部分的访问请求,随着用户数的增长,并发压力主要落在单机的Tomcat上,响应逐渐变慢
系统演进:在多台服务器上分别部署Tomcat,使用反向代理软件(Nginx)把请求均匀分发到每个Tomcat中。此处假设Tomcat最多支持100个并发,Nginx最多支持50000个并发,那么理论上Nginx把请求分发到500个Tomcat上,就能抗住50000个并发。
其中涉及的技术包括:Nginx、HAProxy,两者都是工作在网络第七层的反向代理软件,主要支持http协议,还会涉及session共享、文件上传下载的问题。
(5)第④次演进:数据库读写分离
问题切入:反向代理使应用服务器可支持的并发量大大增加,但并发量的增长也意味着更多请求穿透到数据库,单机的数据库最终成为瓶颈
系统演进:把数据库划分为读库和写库,读库可以有多个,通过同步机制把写库的数据同步到读库,对于需要查询最新写入数据场景,可通过在缓存中多写一份,通过缓存获得最新数据。
其中涉及的技术包括:Mycat,它是数据库中间件,可通过它来组织数据库的分离读写和分库分表,客户端通过它来访问下层数据库,还会涉及数据同步,数据一致性的问题
(6)第⑤次演进:数据库按业务分库
问题切入:业务逐渐变多,不同业务之间的访问量差距较大,不同业务直接竞争数据库,相互影响性能
系统演进:把不同业务的数据保存到不同的数据库中,使业务之间的资源竞争降低,对于访问量大的业务,可以部署更多的服务器来支撑。这样同时导致跨业务的表无法直接做关联分析,需要通过其他途径来解决
(7)第⑥次演进:大表拆分(将大表拆分为小表)
问题切入:随着用户数的增长,单机的写库会逐渐会达到性能瓶颈
系统演进:将一个大数据量的表拆分为多个小表。比如针对评论数据,可按照商品ID进行hash,路由到对应的表中存储;针对支付记录,可按照小时创建表,每个小时表继续拆分为小表,使用用户ID或记录编号来路由数据。只要实时操作的表数据量足够小,请求能够足够均匀的分发到多台服务器上的小表,那数据库就能通过水平扩展的方式来提高性能。就计数实现而言,Mycat也支持在大表拆分为小表情况下的访问控制。
但这种做法显著的增加了数据库运维的难度,对DBA的要求较高。数据库设计到这种结构时,已经可以称为分布式数据库,但是这只是一个逻辑的数据库整体,数据库里不同的组成部分是由不同的组件单独来实现的,如分库分表的管理和请求分发,由Mycat实现,SQL的解析由单机的数据库实现,读写分离可能由网关和消息队列来实现,查询结果的汇总可能由数据库接口层来实现等等,这种架构其实是MPP(大规模并行处理)架构的一类实现。
目前开源和商用都已经有不少MPP数据库,开源中比较流行的有Greenplum、TiDB、Postgresql XC、HAWQ等,商用的如南大通用的GBase、睿帆科技的雪球DB、华为的LibrA等等,不同的MPP数据库的侧重点也不一样,如TiDB更侧重于分布式OLTP场景,Greenplum更侧重于分布式OLAP场景,这些MPP数据库基本都提供了类似Postgresql、Oracle、MySQL那样的SQL标准支持能力,能把一个查询解析为分布式的执行计划分发到每台机器上并行执行,最终由数据库本身汇总数据进行返回,也提供了诸如权限管理、分库分表、事务、数据副本等能力,并且大多能够支持100个节点以上的集群,大大降低了数据库运维的成本,并且使数据库也能够实现水平扩展。
(8)第⑦次演进:使用LVS或F5来使多个Nginx负载均衡
问题切入:数据库和Tomcat都能够水平扩展,可支撑的并发大幅提高,随着用户数的增长,最终单机的Nginx会成为瓶颈
系统演进:由于瓶颈在Nginx,因此无法通过两层的Nginx来实现多个Nginx的负载均衡。
图中的LVS和F5是工作在网络第四层的负载均衡解决方案,其中LVS是软件,运行在操作系统内核态,可对TCP请求或更高层级的网络协议进行转发,因此支持的协议更丰富,并且性能也远高于Nginx,可假设单机的LVS可支持几十万个并发的请求转发;F5是一种负载均衡硬件,与LVS提供的能力类似,性能比LVS更高,但价格昂贵。
由于LVS是单机版的软件,若LVS所在服务器宕机则会导致整个后端系统都无法访问,因此需要有备用节点。可使用keepalived软件模拟出虚拟IP,然后把虚拟IP绑定到多台LVS服务器上,浏览器访问虚拟IP时,会被路由器重定向到真实的LVS服务器,当主LVS服务器宕机时,keepalived软件会自动更新路由器中的路由表,把虚拟IP重定向到另外一台正常的LVS服务器,从而达到LVS服务器高可用的效果。
此处需要注意的是,上图中从Nginx层到Tomcat层这样画并不代表全部Nginx都转发请求到全部的Tomcat,在实际使用时,可能会是几个Nginx下面接一部分的Tomcat,这些Nginx之间通过keepalived实现高可用,其他的Nginx接另外的Tomcat,这样可接入的Tomcat数量就能成倍的增加
(9)第⑧次演进:通过DNS轮询实现机房间的负载均衡
问题切入:由于LVS也是单机的,随着并发数增长到几十万时,LVS服务器最终会达到瓶颈,此时用户数达到千万甚至上亿级别,用户分布在不同的地区,与服务器机房距离不同,导致了访问的延迟会明显不同
系统演进:在DNS服务器中可配置一个域名对应多个IP地址,每个IP地址对应到不同的机房里的虚拟IP。当用户访问www.taobao.com时,DNS服务器会使用轮询策略或其他策略,来选择某个IP供用户访问。此方式能实现机房间的负载均衡,至此,系统可做到机房级别的水平扩展,千万级到亿级的并发量都可通过增加机房来解决,系统入口处的请求并发量不再是问题
(10)第⑨次演进:引入NoSQL数据库和搜索引擎等技术
问题切入:随着数据的丰富程度和业务的发展,检索、分析等需求越来越丰富,单单依靠数据库无法解决如此丰富的需求
系统演进:当数据库中的数据多到一定规模时,数据库就不适用于复杂的查询了,往往只能满足普通查询的场景。对于统计报表场景,在数据量大时不一定能跑出结果,而且在跑复杂查询时会导致其他查询变慢,对于全文检索、可变数据结构等场景,数据库天生不适用。因此需要针对特定的场景,引入合适的解决方案。
如对于海量文件存储,可通过分布式文件系统HDFS解决
如对于key value类型的数据,可通过HBase和Redis等方案解决
如对于全文检索场景,可通过搜索引擎如ElasticSearch解决
如对于多维分析场景,可通过Kylin或Druid等方案解决
当然,引入更多组件同时会提高系统的复杂度,不同的组件保存的数据需要同步,需要考虑一致性的问题,需要有更多的运维手段来管理这些组件等
(11)第⑩次演进:大应用拆分(将大应用拆分为小应用)
问题切入:引入更多组件解决了丰富的需求,业务维度能够极大扩充,随之而来的是一个应用中包含了太多的业务代码,业务的升级迭代变得困难
系统演进:按照业务板块来划分应用代码,使单个应用的职责更清晰,相互之间可以做到独立升级迭代。对于应用之间可能会涉及到一些公共配置的问题,可以通过分布式配置中心Zookeeper来解决
(12)第⑪次演进:引入微服务(将复用的功能抽离成微服务)
问题切入:不同应用之间存在共用的模块,由应用单独管理会导致相同代码存在多份,导致公共功能升级时全部应用代码都要跟着升级
系统演进:如用户管理、订单、支付、鉴权等功能在多个应用中都存在,那么可以把这些功能的代码单独抽取出来形成一个单独的服务来管理,这样的服务就是所谓的微服务,应用和服务之间通过HTTP、TCP或RPC请求等多种方式来访问公共服务,每个单独的服务都可以由单独的团队来管理。此外,可以通过Dubbo、SpringCloud等框架实现服务治理、限流、熔断、降级等功能,提高服务的稳定性和可用性
(13)第⑫次演进:引入企业服务总线ESB屏蔽服务接口的访问差异
问题切入:不同服务的接口访问方式不同,应用代码需要适配多种访问方式才能使用服务,此外,应用访问服务,服务之间也可能相互访问,调用链将会变得非常复杂,逻辑变得混乱
系统演进:通过ESB统一进行访问协议转换,应用统一通过ESB来访问后端服务,服务与服务之间也通过ESB来相互调用,以此降低系统的耦合程度。
这种单个应用拆分为多个应用,公共服务单独抽取出来来管理,并使用企业消息总线来解除服务之间耦合问题的架构,就是所谓的SOA(面向服务)架构,这种架构与微服务架构容易混淆,因为表现形式十分相似。
可以理解为:微服务架构更多是指把系统里的公共服务抽取出来单独运维管理的思想,而SOA架构则是指一种拆分服务并使服务接口访问变得统一的架构思想,SOA架构中包含了微服务的思想
(14)第⑬次演进:引入容器化技术实现运行环境隔离与动态服务管理
问题切入:业务不断发展,应用和服务都会不断变多,应用和服务的部署变得复杂,同一台服务器上部署多个服务还要解决运行环境冲突的问题,此外,对于如大促这类需要动态扩缩容的场景,需要水平扩展服务的性能,就需要在新增的服务上准备运行环境,部署服务等,运维将变得十分困难
系统演进:目前最流行的容器化技术是Docker,最流行的容器管理服务是Kubernetes(K8S),应用/服务可以打包为Docker镜像,通过K8S来动态分发和部署镜像。Docker镜像可理解为一个能运行你的应用/服务的最小的操作系统,里面放着应用/服务的运行代码,运行环境根据实际的需要设置好。把整个“操作系统”打包为一个镜像后,就可以分发到需要部署相关服务的机器上,直接启动Docker镜像就可以把服务起起来,使服务的部署和运维变得简单。
在大促的之前,可以在现有的机器集群上划分出服务器来启动Docker镜像,增强服务的性能,大促过后就可以关闭镜像,对机器上的其他服务不造成影响(在没有引入容器化技术之前,服务运行在新增机器上需要修改系统配置来适配服务,这会导致机器上其他服务需要的运行环境被破坏)
(15)第⑭次演进:以云平台承载系统
问题切入:使用容器化技术后服务动态扩缩容问题得以解决,但是机器还是需要公司自身来管理,在非大促的时候,还是需要闲置着大量的机器资源来应对大促,机器自身成本和运维成本都极高,资源利用率低
系统演进:系统可部署到公有云上,利用公有云的海量机器资源,解决动态硬件资源的问题,在大促的时间段里,在云平台中临时申请更多的资源,结合Docker和K8S来快速部署服务,在大促结束后释放资源,真正做到按需付费,资源利用率大大提高,同时大大降低了运维成本。
所谓的云平台,就是把海量机器资源,通过统一的资源管理,抽象为一个资源整体,在之上可按需动态申请硬件资源(如CPU、内存、网络等),并且之上提供通用的操作系统,提供常用的技术组件(如Hadoop技术栈,MPP数据库等)供用户使用,甚至提供开发好的应用,用户不需要关系应用内部使用了什么技术,就能够解决需求(如音视频转码服务、邮件服务、个人博客等)。在云平台中会涉及如下几个概念:
- **IaaS:**基础设施即服务。对应于上面所说的机器资源统一为资源整体,可动态申请硬件资源的层面;
- **PaaS:**平台即服务。对应于上面所说的提供常用的技术组件方便系统的开发和维护;
- **SaaS:**软件即服务。对应于上面所说的提供开发好的应用或服务,按功能或性能要求付费。
(16)架构设计总结
常见架构设计问题分析
- **架构的调整是否必须按照上述演变路径进行?**不是的,以上所说的架构演变顺序只是针对某个侧面进行单独的改进,在实际场景中,可能同一时间会有几个问题需要解决,或者可能先达到瓶颈的是另外的方面,这时候就应该按照实际问题实际解决。如在政府类的并发量可能不大,但业务可能很丰富的场景,高并发就不是重点解决的问题,此时优先需要的可能会是丰富需求的解决方案。
- **对于将要实施的系统,架构应该设计到什么程度?**对于单次实施并且性能指标明确的系统,架构设计到能够支持系统的性能指标要求就足够了,但要留有扩展架构的接口以便不备之需。对于不断发展的系统,如电商平台,应设计到能满足下一阶段用户量和性能指标要求的程度,并根据业务的增长不断的迭代升级架构,以支持更高的并发和更丰富的业务。
- **服务端架构和大数据架构有什么区别?**所谓的“大数据”其实是海量数据采集清洗转换、数据存储、数据分析、数据服务等场景解决方案的一个统称,在每一个场景都包含了多种可选的技术,如数据采集有Flume、Sqoop、Kettle等,数据存储有分布式文件系统HDFS、FastDFS,NoSQL数据库HBase、MongoDB等,数据分析有Spark技术栈、机器学习算法等。总的来说大数据架构就是根据业务的需求,整合各种大数据组件组合而成的架构,一般会提供分布式存储、分布式计算、多维分析、数据仓库、机器学习算法等能力。而服务端架构更多指的是应用组织层面的架构,底层能力往往是由大数据架构来提供。
有没有一些架构设计原则?
- N+1设计:系统中的每个组件都应做到没有单点故障;
- 回滚设计:确保系统可以向前兼容,在系统升级时应能有办法回滚版本;
- 禁用设计:应该提供控制具体功能是否可用的配置,在系统出现故障时能够快速下线功能;
- 监控设计:在设计阶段就要考虑监控的手段;
- 多活数据中心设计:若系统需要极高的高可用,应考虑在多地实施数据中心进行多活,至少在一个机房断电的情况下系统依然可用;
- 采用成熟的技术:刚开发的或开源的技术往往存在很多隐藏的bug,出了问题没有商业支持可能会是一个灾难;
- 资源隔离设计:应避免单一业务占用全部资源;
- 架构应能水平扩展:系统只有做到能水平扩展,才能有效避免瓶颈问题;
- 非核心则购买:非核心功能若需要占用大量的研发资源才能解决,则考虑购买成熟的产品;
- 使用商用硬件:商用硬件能有效降低硬件故障的机率;
- 快速迭代:系统应该快速开发小功能模块,尽快上线进行验证,早日发现问题大大降低系统交付的风险;
- 无状态设计:服务接口应该做成无状态的,当前接口的访问不依赖于接口上次访问的状态;
⑪⑫⑬⑭⑮⑯⑰⑱