文章目录
俗话说,鱼与熊掌不可兼得,软件设计时也是一样,处处存在了权衡。
在设计一个系统的架构时,我们需要决定这个系统要包含哪些功能以及可以忽略哪些功能。我们做出的每个选择都是权衡的结果。
在本文中,我们将探讨系统架构设计中15
个最常见的权衡问题,并使用实际例子进行说明:
1. 可扩展性与性能
可扩展性
指的是系统在用户数量或数据量增长时,能否轻松扩展以处理更多的流量和数据,以及能够轻松的扩展新的功能点。
性能
则是指系统的响应速度,“对于用户的请求,系统能在多长时间内返回正确的响应给用户?”
向系统添加更多机器可以使其更具可扩展性,但是管理这些机器和协调任务的复杂性可能会导致性能的降低。或者使用不少设计模式使得扩展性增强,但是处理链路变长,影响性能。
因此这两个指标通常相互矛盾,改进其中一个可能会影响另一个,因此需要您做出对系统最有利的选项,是牺牲一些性能以换取更好的扩展性?还是降低系统的可扩展性以换取系统更好的性能?这取决于具体的业务和系统。
例如,对于高频交易系统,要求极低的延迟,交易速度直接关系到利润,因此这些系统必须在毫秒级别内完成交易指令的处理,为了性能,可能会使用专用的硬件和网络基础设施,而在扩展性方面做出牺牲。
而对于Web
应用、社交媒体平台、电商网站等大型互联网服务,这些系统需要能够轻松扩展,以应对突发的流量增长(例如淘宝双十一)。因此为了提高系统的可扩展性,可能会在不太重要的接口上牺牲一些性能,
2. 垂直扩展与水平扩展
垂直扩展
指的是通过向现有服务器添加更多或性能更好的资源(如CPU
、内存)来提升系统性能。
而水平扩展
则是通过增加更多服务器来分担系统负载。
垂直扩展更简单,但单台计算机再怎么升级也是存在上限的,而且存在单点故障问题。如果机器发生故障,可能会导致整个系统都不可用。水平扩展允许几乎无限的扩展,但同时带来了管理分布式系统的复杂性。
初创公司可能会通过增加CPU
和RAM
来垂直扩展其服务器以应对增加的负载。但是随着公司业务的发展,后续一般会转向水平扩展,通过增加服务器来分散负载,这其中可能会需要将当前的服务架构进行重构,这也是为什么初期不采用水平扩展的原因之一。
3. 延迟与吞吐量
延迟
是指从一个请求发出到收到响应所花费的时间,通常以毫秒(ms
)为单位。延迟反映了系统对单个请求的响应速度。延迟越低,响应速度越快。
吞吐量
是指在一定时间内系统能够处理的请求数量,通常以“每秒请求数”(requests per second, RPS
)或“每秒事务数”(transactions per second, TPS
)为单位。假设一个网站服务器每秒能够处理100
个用户请求,那么这个服务器的吞吐量就是100 RPS
。吞吐量反映了系统的整体处理能力。吞吐量越高,系统在单位时间内处理的请求就越多。
延迟和吞吐量通常是互相影响的。系统在高吞吐量下可能会增加延迟,因为处理大量请求会导致资源(如CPU
、内存、网络带宽)被更多地占用,从而增加每个请求的响应时间。
低延迟系统对于实时应用至关重要,如果延迟太高,用户体验会不好,因为用户发出请求后需要等待很长时间才能拿到结果,例如,在线游戏要求低延迟,以确保玩家之间的互动是实时的。
对于视频流媒体服务(优酷、抖音),吞吐量则是关键。因为在高峰时段,可能有数千万用户同时观看视频。高吞吐量能够确保所有用户都能流畅播放视频,而不会出现卡顿。
4. 关系数据库 与 NoSQL 数据库
关系数据库
建立在关系模型之上,将数据组织为由行和列构成的表格,并通过唯一的键(主键)来标识每一行。这些数据库结构严谨,提供强大的查询语言,非常适合处理复杂的查询和事务。例如,在一个电商平台上,用户信息可以存储在“用户”表中,订单信息可以存储在“订单”表中,这些表之间通过用户ID
(主键)建立关系,来关联用户与其订单。
我们经常使用的MySQL
就是一种关系数据库。然而,关系数据库在水平扩展上可能具有挑战性。
NoSQL
(非关系数据库) 是一类不依赖固定表格模式的数据库,用于存储和处理大量的非结构化或半结构化数据。相对关系型数据库,NoSQL
数据库提供了更大的灵活性,并且更容易扩展,但通常会在查询功能和ACID
事务上做出一些妥协。
NoSQL
遵循BASE
属性,即基本可用、软状态、最终一致性……例如,它可能无法始终满足一致性,但最终会变得一致。因此,它牺牲了 ACID
属性,但并非完全牺牲!
常见的NoSQL
数据库包括MongoDB
、Cassandra
、Redis
和Couchbase
。
在社交媒体应用中,用户生成的帖子、评论和点赞数据可以存储在一个NoSQL
数据库中,如MongoDB
,每个用户的动态数据可以以文档形式存储,不必像关系型数据库那样进行严格的结构定义。
5.一致性与可用性(CAP定理)
一致性
指的是在一个分布式系统中,所有节点在同一时刻看到的数据是相同的。换句话说,当一个数据更新后,所有访问该数据的用户都会看到相同的最新值。
例如在金融系统中,每一笔交易都必须准确无误地反映在账户余额中,不能出现任何差错。当用户进行转账操作后,账户余额必须立即更新并一致地显示在所有系统中。
可用性
就是确保系统始终正常运行,即使系统的某些部分出现问题。
例如你在一个电商网站上购物,即使这个网站的部分服务出现问题,你依然能够浏览商品、下单和付款,这就是系统的可用性。
根据CAP
理论,在分布式系统中,你只能保证一致性、可用性和分区容忍性中的两个。
什么是 CAP 定理?
在一致性和可用性之间进行选择取决于您的系统想要提供的用户体验什么更重要。
一致性优先:适合金融系统、银行账户管理、库存管理等需要确保数据准确性的场景。在这些系统中,数据的一致性至关重要,稍有偏差可能会导致严重的后果。
可用性优先:适合社交媒体、搜索引擎和电商网站的非关键数据场景。这些系统需要确保高可用性,即使有时数据可能不一致,也必须确保系统持续响应用户请求。
6. 强一致性与最终一致性
强一致性
意味着在一个分布式系统中,数据的更新在所有节点上同时生效,也就是说,一旦数据被更新,所有后续的读取操作—无论用户从哪个节点读取数据,都会得到相同的最新值。
假设你在网上银行转账后,立即查看账户余额,强一致性确保你看到的余额已经包含了刚刚的转账金额,无论你从哪个设备或位置访问银行账户。
而最终一致性
指的是在分布式系统中,数据的更新不会立即同步到所有节点,而是经过一段时间后,所有节点的数据最终会达到一致,在这之前允许短暂的数据不一致。
例如在社交媒体上,你的朋友修改了一条状态信息,最开始你可能看到的还是他的旧状态,过来几秒钟后看到更新后的状态。这种情况下,系统是最终一致的:虽然在一开始数据不一致,但最终所有人看到的状态是一致的。
选择强一致性还是最终一致性,有以下两点参考:
强一致性:适合金融交易系统、银行账户管理和库存管理等场景,这些系统需要确保数据在所有节点上的一致性和实时性,任何延迟或不一致都可能带来严重后果。
最终一致性:适合社交媒体、电商购物车和内容分发网络等场景,这些系统允许在短时间内存在数据的不一致性,但最终会达成一致,以确保系统的高可用性和扩展性。
7. Read-Through vs Write-Through Cache
缓存是一种通过将经常访问的数据存储在更快的存储介质中来加快数据访问的技术。说到缓存策略,“Read-Through”
和“Write-Through”
是两种常见的策略。
Read-Through策略
: 更新数据时,仅将数据更新写入数据库,缓存则依赖后续读取时写入,客户端在请求数据时应用程序会先检查缓存。如果数据不存在(缓存未命中),则Cache
会将数据从较慢的数据库中加载到自身中,然后再返回给应用程序。
它适用于读取频繁但不经常更新的应用程序。
例如一个新闻网站可能每天有数百万用户访问相同的文章,但这些文章的内容很少被更新。这种情况下,Read-Through
模式是一种很好的选择,只有第一次读取文章的用户稍微慢点(因为第一次读取时不在缓存中,需要从数据库加载),此后所有的用户都能直接从缓存拿到文章数据了,这个性能是很高的。
Write-Through策略
:更新数据时,同时将数据更新写入缓存和数据库,确保数据最新并降低数据丢失风险。
这对于一致性要求高的应用程序非常有益。所有写入都会立即反映在缓存和主存储中。
例如电影票预订系统使用Write-Through
策略,通过立即在缓存和数据库中记录预订来防止超额预订。
8.批处理与流处理
批处理
是指将数据积累到一定量后,集中一次性处理。这种方式通常在预定的时间间隔内处理大量数据。
在电商平台上,每天或每周会对用户的浏览和购买行为进行分析,以便制定营销策略。数据量较大,不需要实时处理,因此使用批处理更为合适。
流处理
是指对数据进行实时或近实时的处理。数据在生成后立即被处理,不需要等待积累到一定量。
假设你在银行工作,系统需要实时监控所有的交易以检测是否存在欺诈行为。这些交易数据在发生时会立即被处理和分析,这就是流处理。
在设计系统时,需要根据具体的应用场景和需求,选择使用批处理还是流处理,或者在两者之间进行结合。
批处理
:适合那些不需要实时性、可以集中处理大量数据的系统,比如数据仓库、离线分析、定期账单生成等。这些系统可以利用批处理的高效率,在预定时间处理积累的大量数据。
流处理
:适合需要实时性、持续处理数据的系统,比如实时监控、在线推荐和欺诈检测系统。这些系统必须对数据进行实时处理,以快速响应用户或系统的需求。
9.同步与异步处理
同步处理
是指任务一个接一个地执行。必须完成一个任务才能开始下一个任务,系统会等待结果后再继续执行。
例如你去银行办业务,排队等候叫号,每个人必须等到前一个人办完业务,自己才能开始。这就是同步处理。
异步处理
允许任务在后台运行,不需要等待其完成就可以开始本任务。
在社交媒体上上传照片是在后台异步进行的。您可以在照片上传时继续滚动或退出应用程序。
10.有状态系统与无状态系统
有状态系统
是指服务在不同的请求之间保持状态信息,当前请求可能会依赖前一个请求的结果。
在线购物时,当您将商品添加到购物车时,网站会记住您的选择。如果您离开以浏览更多商品,然后返回购物车,您的商品仍在那里,等待您结账。
无状态系统
是指每个请求都是独立的,服务不需要保存前一个请求的状态信息。每个请求本身已经包含所有必要的信息,服务在处理完请求后不会保存任何数据。
例如搜索引擎,每次搜索请求都是独立的,系统不需要记住用户之前的搜索内容。
关于选择有有状态架构还是无状态架构,下面的例子可以作为参考:
哪些系统需要有状态架构?
在线游戏服务器:玩家的游戏进度、装备等信息需要在多次游戏会话之间保持,系统需要记住玩家的状态。
视频会议系统:在视频会议中,系统需要保持参与者的状态,如音视频流的连接、通话记录等。
哪些地方需要无状态架构?
RESTful API
:很多网络服务使用RESTful API
来处理请求,每个请求都是独立的,系统不需要记住用户的状态,适合无状态架构。
静态内容分发
:例如CDN
(内容分发网络),在用户请求静态资源(如图片、CSS
文件)时,服务器只需简单返回资源,不需要记住用户的任何信息。
11. 长轮询与 WebSockets
长轮询
是一种技术,客户端向服务器请求数据,服务器保持请求打开,直到有新数据时才响应。客户端收到数据后再发起下一次请求,从而实现实时数据推送(更准确的讲只能做到准实时)。
这种方式技术实现较为简单,适用于简单的实时数据更新场景,例如社交媒体平台的通知系统。浏览器不断向服务器查询新通知,当出现新通知或发生超时时,服务器会做出响应。
WebSockets
是一种真正的双向通信协议,客户端和服务器之间可以持续保持连接,双方都可以主动发送数据,而不必等待请求。这种方式更高效、实时性更强。
这种方式适合需要高实时性、持续数据交换的场景,例如在多人在线游戏中,WebSockets
通过客户端和服务器之间的持久连接,实时共享玩家动作和游戏更新。
12. 规范化与非规范化
数据库设计中的规范化涉及将数据拆分到相关表中,以确保每条信息只存储一次。其目的是减少冗余并提高数据完整性。
例如客户相关的信息可以有两个独立的表:一个用于客户详细信息,另一个用于订单,避免每个订单的客户信息重复。
另一方面,非规范化是将数据重新组合到更少的表中以提高查询性能。这通常会在数据库中引入冗余(重复信息)。
例如博客网站可以将最新评论与帖子存储在同一张表中(非规范化),以加快帖子和评论的显示,而不是将它们分开存储(规范化)。
13. 单体架构与微服务架构
单体架构
将应用的所有功能作为单一、不可分割的单元运行。所有功能都打包在一个进程中,通常作为单一代码库进行开发、测试和部署。
而微服务架构
则是将应用分解为一组小型、松散耦合的服务,每个服务都专注于特定功能,并通过轻量级通信协议(如HTTP
)相互交互。
单体架构简单易部署,适合小型应用程序或团队。但是,随着应用程序的增长,它可能会减慢开发速度并使可扩展性变得复杂。微服务架构提高了可扩展性和开发速度。但是,它引入了服务管理、数据一致性的复杂性,并增加了通信开销。
为简单起见,小型 Web
应用程序可能以单体式架构开始。随着规模的增长,它可以演变为微服务架构,拆分为更小、可独立扩展的服务,从而实现更好的灵活性和可扩展性。
14. REST 与 GraphQL
REST
是一个成熟的API
标准,提供了简洁性并支持多种格式。使用REST API
时,您可以通过访问多个端点来收集数据。
GraphQL
提供了更高效的数据获取,减少了请求次数,但需要更高的学习曲线和更多的前期设计。
在 GraphQL
中,您可以向 GraphQL
服务器发送一个包含具体数据需求的查询。服务器然后返回一个JSON
对象,其中包含了这些需求的数据。
15. TCP 与 UDP
TCP
和 UDP
是TCP/IP
协议中传输层的两种协议。
TCP(传输控制协议)
可确保您的消息完好无损地按照发送的顺序到达。它在发送方和接收方之间建立连接,检查数据是否正确接收,必要时会重新发送丢失的数据。TCP
非常适合用于对可靠性要求很高的应用程序,比如电子邮件服务。
UDP(用户数据报协议)
牺牲了可靠性来换取速度,适用于视频流等对时间敏感的应用程序,在这种情况下,传输过程中丢失一些数据也没关系。UDP
发送数据时无需建立连接,也不检查数据是否接收或顺序是否正确。在线游戏和直播服务可能会选择 UDP
,因为它的延迟较低,牺牲可靠性来换取速度。
总结
每个权衡的选择因具体项目而异。
理解这些权衡可以帮助你作出明智的选择来设计高性能、可扩展和用户体验优秀的系统。