设计一个支持数百万用户的分布式系统是一项复杂的任务,需要不断改进和完善。
最近我读了 Alex Xu 的一本书,名为《 系统设计面试——内幕指南 》。本文受该书第一部分的启发,分享了许多大型科技公司用来扩展其架构以支持多达一百万用户的一些流行技术。
这不是一个详尽的清单,但如果你是一个刚刚起步的新开发人员,这可以帮助你为你的职业生涯打下更坚实的基础。
使用负载均衡器
负载均衡器是一种在多个 Web 服务器之间均匀分配网络流量的设备。在这种架构中,客户端不直接连接到服务器,而是连接到负载均衡器的公共 IP。
使用负载平衡器还可以在 Web 服务器发生故障时保护您的网站,从而提高可用性。例如,
- 如果一台服务器出现故障,所有流量都可以路由到第二台服务器。这可以防止整个系统离线。
- 如果将来流量增长,而这两台服务器不足以正确处理所有请求,那么您只需向 Web 服务器池中添加更多服务器,负载均衡器就会自动开始向它们分发请求。
负载平衡算法
让我们看一下负载均衡器可以使用从池中为传入请求选择 Web 服务器的一些算法:
- 循环赛 ——从池中的第一个服务器开始,然后移动到下一个服务器,当您完成最后一个服务器后,您将回到第一个服务器,然后再次开始按照池子进行工作。
- 基于负载的服务器 ——根据当前负载最小的服务器来分配服务器,从而提高吞吐量。
- IP 哈希 - 通过对传入请求的 IP 地址进行哈希处理并使用哈希值对服务器池中可用的服务器数量进行模运算来分配服务器。
使用缓存
缓存存储了先前响应的结果,以便可以更快地处理对相同数据的任何后续请求。因此,您可以使用缓存来最大限度地减少系统的网络延迟。
通过减少对数据库的网络调用,您可以显著提高应用程序的性能。这是因为重复的数据库调用成本高昂且耗时。
例如,每次新用户加载网站主页时,都会进行一次或多次数据库调用来获取数据。这会增加响应时间。缓存可以通过存储您知道会经常调用的结果和很少修改结果的结果来缓解此问题。
在使用缓存之前,请记住以下几点注意事项:
- 设置过期策略: 您应该始终为缓存设置过期策略。如果没有,数据将永久存储在缓存中,并且会变得陈旧。
- 同步缓存和数据库: 您应该建立一种机制来保持数据库和缓存同步。如果数据库中发生任何数据修改操作,而相同的更改未反映在缓存中,则会导致系统出现不一致。
- 设置驱逐策略 :您应该有一个算法,可以决定在缓存已满并且您收到将其他项目添加到缓存的请求时将删除哪些现有项目。最近最少使用 (LRU) 是当今最流行的缓存驱逐策略之一。
使用内容分发网络 (CDN)
CDN 或内容分发网络是地理分布的服务器网络,有助于从性能角度改善静态内容的分发。CDN 服务器通常用于缓存图像、CSS 和 JavaScript 文件等内容。
CDN 的工作原理如下:
- 当客户端发送请求时,CDN 服务器将向客户端提供与该请求相关的所有静态内容。
- 如果 CDN 服务器没有所需文件,它就会向原始 Web 服务器发送请求。
- CDN 缓存文件并将其返回给客户端。
- 假设现在另一个客户端发送相同的请求,那么该文件将从 CDN 返回。
在使用 CDN 之前,请记住以下几点注意事项:
- 成本 :CDN 通常由第三方提供商运营,他们会向您收取进出 CDN 的数据传输费用。因此,缓存不常用的资产不应存储在 CDN 中。
- 回退机制 :如果 CDN 发生故障,您应该能够检测到它并开始从原始 Web 服务器发送资源请求。因此,您应该构建一种机制来应对 CDN 故障。
设置消息队列
消息队列允许异步通信。它充当消息的缓冲区,将消息存储在队列中,直到被处理为止。
消息队列的架构包括一个输入服务(称为发布者),该服务创建消息、将其发布到消息队列并发送事件。另一个服务(称为订阅者)接收这些事件并执行消息定义的操作。
发布者和订阅者彼此分离,这使得消息队列成为构建可扩展应用程序的首选架构。
消息队列示例
请考虑以下用例:
您正在构建一个用于订票的应用程序。用户完成预订后,应立即触发一条确认其付款和票证的消息。此任务可能需要一些时间才能完成,并且不应让我们的系统等待处理下一个请求。
在这里,我们可以将消息详细信息连同用户电话号码等其他元数据一起推送到消息队列。另一个工作服务从消息队列中获取作业并异步执行消息创建和发送任务。
发布者和订阅者可以独立扩展。当队列的大小增加时,您可以添加更多消费者以减少处理时间。
明智地选择数据库
根据 维基百科 :
A database is an organized collection of data stored and accessed via a computer system.
数据库用于持久存储数据。我们通常有两种类型的数据库:关系型和非关系型。
关系型数据库
关系数据库在数据库中存储的条目之间存在严格的关系,并且它们高度结构化。这是为了确保数据完整性。例如,如果表的架构不允许添加新字段,则向表中添加新字段将引发错误。
关系数据库的另一个重要特性是ACID事务。
ACID 事务
这些是描述良好关系数据库应支持的任何给定事务(一组读取或写入操作)的一组特性。
原子性 是指当一个包含多个操作的事务发生时,数据库必须保证如果一个操作失败,整个事务就失败。要么完全失败,要么根本不发生。
一致性 意味着数据库中的每个事务在数据库状态发生改变时不会违反数据完整性约束,也不会破坏数据。简而言之,一致性意味着对于每个“读取”操作,您都会收到最新的“写入”操作结果。
隔离性 意味着您可以在数据库上运行多个并发事务,而不会导致任何类型的不一致。所有这些事务都将彼此独立发生。
持久性 是指一旦事务执行完毕,更新的数据仍然存储在数据库中。它将保存在磁盘上,即使发生系统故障,数据也将持久存在。
非关系数据库
非关系型数据库的结构不太严格,数据库中存储的条目之间可能有或可能没有严格的关系。数据通常以键值对的形式存储。例如:
[
{
firstName: "Apoorv",
lastName: "Tyagi",
gender: "M"
},
{
name: "Judit",
rank: "Polgar",
gender: "F"
},
{
//...
},
]
与关系数据库的 ACID 属性类似,非关系数据库提供 BASE 属性:
基本可用 (BA) 表明系统即使出现多个故障也能保证可用性。
软状态(S) 表示系统的状态可能会随着时间而改变,甚至由于最终一致性而不需要应用程序交互。在 NoSQL 中,与 RDBMS 不同,人们认为数据一致性是开发人员的责任,不应由数据库处理。
最终一致性 (E) 表示系统“最终”会变得一致。但是,无法保证何时会发生这种情况。
NoSQL 与 SQL
如果符合以下条件,非关系数据库(通常也称为 NoSQL 数据库)可能是更好的选择:
- 您的应用程序需要低延迟。因为没有复杂的 JOIN 查询。
- 您拥有大量非结构化数据,或者您的数据之间没有任何关系。
如何扩展数据库
现在让我们看看扩展数据库的各种方法:
垂直与水平数据库扩展
在垂直扩展中,您可以通过向单个服务器添加更多功能(CPU,RAM)来进行扩展。
在水平扩展中,您只需向服务器池中添加更多服务器即可进行扩展。
对于小规模应用程序,垂直扩展因其简单性而是一个很好的选择。但垂直扩展有一个硬性限制。实际上不可能向单个服务器添加无限的 RAM、CPU 和内存。
因此,建议您对大型应用程序进行水平扩展(也称为分片)。
数据库复制
这是将数据从中央数据库复制到一个或多个数据库的过程。
您使用主-副本(以前称为主从)架构进行数据库复制。主数据库通常仅支持写入操作。所有数据修改操作(如插入或更新)都将发送到主数据库。
另一方面,副本数据库从主数据库获取数据的副本,并且仅支持读取操作。所有数据查询操作(如读取、获取)都将由副本数据库提供。
数据库复制的优点:
- 性能改进 :数据库复制显著提高了性能,因为所有写入和更新都发生在主节点上,而所有读取操作都分布到副本节点,从而允许更多查询并行运行。
- 高可用性 :由于我们在世界不同地区的不同节点上创建了数据副本,因此即使一个数据库节点脱机,应用程序仍可正常运行,因为您可以从其他节点访问数据。 如果主节点发生故障,则任何一个副本节点都将提升为主节点并提供写入/更新操作,直到原始主节点重新联机。
包起来
就是这样。感谢您的光临。希望您觉得这篇文章有趣且信息丰富!
如果您想进一步讨论任何技术话题,或者您有任何问题、建议或反馈,我的 DM 始终开放:
- 推特
- GitHub
- 博客
快乐学习!
发表评论 取消回复