要设计一个支持数亿用户的系统并不容易。对于软件架构师来说,这总是一个很大的挑战(但是读完我的文章🤣之后,从今天开始就会变得容易了)
下面是我在本文中讨论的一些主题。
- 从最简单的开始:一应俱全。
- 高可用的艺术:水平扩展,垂直扩展。
- 关系性数据库扩容:主从复制、主主复制、联合、分片、去正规化和SQL调优。
- 数据库选择:NoSQL还是SQL?
- 高级概念:缓存,CDN, geoDNS。等。
今天,我不想讨论高性能计算中的术语,比如容错、可靠性、高可用性等。保持冷静,让我们现在开始!
从0开始
在下面的图中,我想先与一个基本的应用程序。最简单的方法是将整个应用程序部署在一台服务器上。这可能是我们大多数人开始的方式。
- 一个网站(包括api)运行在像Apache¹(或Tomcat²)这样的web服务器上。
- 一个类似Oracle³(或MySQL)的数据库。
我们在同一台物理机器上同时拥有web服务器和数据库服务器
但是当前的架构有以下缺点。
- 数据库故障,系统故障。
- 如果webserver故障,则整个系统故障。
在这种情况下,我们没有故障转移和冗余。如果一个服务器坏了,所有的都坏了。
使用DNS服务器解析主机名和IP地址
在上图中,用户 (或客户端) 访问到DNS⁵ 系统,以获取我们系统的服务器的Internet协议 (IP) 地址。获得ip地址后,请求将直接发送到我们的系统。
每次访问网站时,您的计算机都会执行DNS查询。
通常,域名系统(DNS)服务器是由托管公司提供的付费服务,而不是运行在你自己的服务器上。
高可用的艺术
由于数据量增加、工作量增加(例如事务数量)和用户增长等原因,我们的系统必须进行扩展。
可伸缩性通常意味着能够处理更多的用户、客户端、数据、事务或请求,而不会通过添加更多的资源而影响用户体验。
我们必须决定如何扩展这个系统。在这种情况下,有以下两种类型的扩展:向上扩展和向外扩展。
垂直扩展vs水平扩展
垂直扩展:为服务器增加更多的RAM和CPU
这也被称为“垂直扩展”,它指的是系统的资源最大化,以扩展其处理不断增加的负载的能力——例如,我们通过添加RAM和CPU来增加服务器的能力。
如果我们使用8GB内存运行服务器,只需更换或添加硬件就可以很容易地升级到32GB甚至128GB。
有许多垂直扩展的方法,如下所示。
- 通过在RAID中添加更多的硬盘来增加更多的I/O容量。
- 通过切换到固态驱动器(ssd)来提高I/O访问时间。
- 切换到具有更多处理器的服务器。
- 通过升级网络接口或安装额外的网络接口来提高网络吞吐量。
- 通过增加RAM来减少I/O操作。
垂直扩展对于小型系统来说是一个很好的选择,并且能够负担得起硬件升级,但是它也有以下一些严重的限制。
- “不可能给一台服务器增加无限的内存和cpu”。它主要取决于操作系统和服务器的内存总线宽度。
- 当我们将RAM升级时,必须关闭服务器,因此,如果系统是单机的,停机是不可避免的。
- 功能强大的机器通常比流行的硬件贵很多。
扩展不仅适用于硬件术语,也适用于软件术语,例如,它包括优化查询和代码。
您需要多台服务器吗?
随着用户数量的增长,一台服务器是无法承受这么大的请求量。我们需要考虑将单个服务器分离为多个服务器。
随着用户数量的增长,一台服务器是远远不够的
使用应用和数据库分离,有以下一些优点。
•web服务器可以与数据库服务器进行不同的调优。
•web服务器需要更好的CPU,数据库服务器需要更多的内存。
•使用单独的服务器为网络层和数据层允许他们单独扩容。
水平扩展: 扩展任意数量的硬件和软件
它也被描述为“水平扩展”,我们向资源池添加更多实体(机器、服务)。水平扩展比垂直扩展更难实现,因为我们需要在系统构建之前考虑它。
水平扩展通常一开始的成本更高,因为我们需要更多的服务器来实现最基本的功能,但是在后期会有所回报。我们需要做一些权衡。
•增加服务器数量意味着需要维护更多的资源。
•系统在架构设计适合要考虑完善,以允许在多个服务器之间并行和分配工作。
使用负载均衡器来平衡所有节点之间的流量
负载均衡器是一种专门的硬件或软件组件,它有助于将流量分散到服务器集群上,以提高系统(包括但不限于应用程序、网站或数据库)的响应性和可用性。
通常,负载均衡器位于客户机和服务器之间,接受传入的网络和应用程序流量,并使用各种算法将流量分布到多个后端服务器。所以,它也可以用在很多地方,例如;Web服务器和数据库服务器之间,以及客户机和Web服务器之间。
HAProxy和NGINX是两款流行的开源负载均衡软件。
负载均衡器技术是一种容错保证技术,可以提高可用性,如下所示。
- 如果服务器1挂了,所有流量将被路由到服务器2和服务器3。网站不会下线。您还需要向服务器池添加一个新的正常运行的服务器,以平衡负载。
- 当流量快速增长时,你只需要向web服务器池中添加更多的服务器,负载均衡器就会为你路由流量。
负载均衡器采用各种策略和工作分配算法来优化负载分配,如下所示。
- 轮询:在这种情况下,每个服务器按照顺序接收请求,类似于先进先出(FIFO)。
- 连接数最少:连接数最少的服务器将接收到请求。
- 最快的响应时间:具有最快响应时间(最近或经常)的服务器将被定向到请求。
- 加权策略:性能更强大的服务器将接收更多的请求,而较弱的服务器将接受更少的请求。
- IP哈希:在这种情况下,计算客户端IP地址的哈希值,将请求重定向到服务器。
在多个服务器之间平衡请求的最简单方法是使用硬件设备。
- 立即增加或删除共享ip的服务器。
- 负载均衡可以根据需要进行。
软件负载平衡是硬件负载平衡器的廉价替代品。它在第4层(网络层)和第7层(应用层)运行。
- 第四层:负载均衡器使用TCP在网络层提供的信息。在这一层,它通常关注请求的内容就选择一个服务器进行转发。
- 第七层:请求可以根据查询字符串、cookie或我们选择的任何头中的信息以及包括源地址和目的地址在内的常规层信息进行平衡。
扩展关系数据库
使用简单的系统,我们可以使用像Oracle或MySQL这样的RDBMS来保存数据。但是我们需要扩展它们时,关系数据库系统会面临很多挑战。
扩展关系数据库有许多技术: 主从复制、主主复制、联合、分片、非规范化和SQL优化。
- 复制允许我们在不同的机器上存储相同数据的多个副本。
- 功能分区能分割数据库。
- 分片是一种与分区相关的数据库架构模式,将数据的不同部分放到不同的服务器上,不同的用户将访问数据集的不同部分
- 反规范化尝试以牺牲一些写性能为代价,通过复制写入多个表中的数据来提高读性能,以避免昂贵的连接。
- SQL校准。
主从复制
主从复制技术使来自一个数据库服务器 (主服务器) 的数据能够被复制到一个或多个其他数据库服务器 (从服务器),如下图所示。
对主服务器进行更新
- 客户端将连接到主服务器并更新数据。
- 然后,数据将在从属服务器中同步,直到所有数据在服务器之间保持一致。
实际上,这里仍然存在一些瓶颈。
- 如果主服务器因任何原因出现故障,数据仍可通过从服务器获得,但无法进行新的写入。
- 我们需要一个额外的算法来将从机提升到主机。
以下是实现只有一台服务器可以处理更新请求的一些解决方案。
- 同步解决方案: 在所有服务器 (分布式事务) 接受之前,不会提交数据修改事务,因此在故障转移时不会丢失数据。
- 异步解决方案: 提交-> 延迟-> 传播到群集中的其他服务器,因此故障转移时可能会丢失一些数据更新。
请记住,如果同步解决方案太慢,请更改为异步解决方案。
主-主复制
每个数据库服务器都可以充当主服务器。在某个时间点,所有的主节点同步,以确保它们都有正确和最新的数据。
所有节点读写所有数据,下面是主-主复制的一些优点。
- 如果一个主服务器发生故障,其他数据库服务器可以正常运行,填补空白。当数据库服务器重新联机时,它将使用复制恢复正常。
- 主机可以位于多个物理站点,可以分布在整个网络中。
受限于主机处理更新的能力。
按功能分区
联邦(或功能分区)按功能分割数据库。例如,您可以拥有三个数据库:论坛、用户和产品,而不是单一的单一数据库,从而减少对每个数据库的读写流量,从而减少复制延迟。
分区按函数分割数据库
更小的数据库可在内存中加载更多的数据,增加了缓存命中的几率。由于没有串行的写入,您可以并行地写,从而提高吞吐量。
分片
分片(也称为数据分区)是一种将一个大数据库分解成许多小部分的技术,这样每个数据库只能管理数据的一个子集。
在理想的情况下,我们有不同的用户都与不同的数据库节点通信。它有助于提高系统的可管理性、性能、可用性和负载平衡。
- 每个用户只需要与一个服务器交谈,因此从该服务器得到快速响应。
- 服务器之间的负载很好地平衡——例如,如果我们有5个服务器,每个服务器只需要处理20%的负载。
在实践中,有许多不同的技术可以将数据库分解成多个更小的部分。
水平分区
在这种技术中,我们将不同的行放入不同的表中。例如,如果我们在一个表中存储用户配置文件,那么我们可以决定id小于1000的用户存储在一个表中,id大于1001和小于2000的用户存储在另一个表中
垂直分区
**
在本例中,我们将数据划分为与特定特性相关的表存储在自己的服务器中。例如,如果我们正在构建一个类似instagram的系统——我们需要存储与用户、他们上传的照片和他们关注的人相关的数据——我们可以决定将用户的个人资料信息放在一个数据库服务器上,朋友列表放在另一个服务器上,而照片放在第三个服务器上。
基于目录的分区
解决这个问题的一种松散耦合方法是创建一个查找服务,该服务知道您当前的分区模式,并保存每个实体的映射以及它存储在哪个数据库碎片上。
当数据存储可能需要扩展到单个存储节点可用资源之外,或者通过减少数据存储中的争用来提高性能时,我们可以使用这种方法。但是请记住,切分技术存在以下一些常见问题。
- 数据库连接变得更昂贵,在某些情况下不可行。
- 分片会损害数据库引用完整性。
- 数据库模式更改可能会变得非常昂贵。
- 数据分布不均匀,shard上有很多负载。
反模式化
反规范化试图以牺牲一些写性能为代价来提高读性能。数据的冗余副本被写入多个表中,以避免昂贵的连接。
一旦数据通过联合和分片等技术进行分布,跨数据中心的连接管理将进一步增加复杂性。反模式化可能会避免这种复杂的连接。
在大多数系统中,读的数量可能大大超过写的数量💯1甚至1000:1。导致复杂数据库连接的读取可能非常昂贵,会在磁盘操作上花费大量时间。
一些RDBMS如PostgreSQL和Oracle支持物化视图,物化视图处理存储冗余信息和保持冗余副本一致的工具。
使用哪个数据库?
在数据库领域,有两种主要的解决方案:SQL和NoSQL。它们都有不同的构建方式,不同的信息存储类型,以及它们使用的存储方法。
SQL
关系数据库以行和列的形式存储数据。每一行包含关于一个实体的所有信息,每一列包含所有单独的数据点。
一些最流行的关系数据库是MySQL、Oracle、MS SQL Server、SQLite、Postgres和MariaDB。
NoSQL
它也称为非关系数据库。这些数据库通常分为五个主要类别: 键值、图形、列、文档和Blob存储。
Key-Value 存储
数据存储在键-值对数组中。通过‘key’可以找到‘value’。
著名的键值存储包括Redis、Voldemort和Dynamo。
**
文档数据库**
在这些数据库中,数据存储在文档中(而不是表中的行和列),这些文档被分组在集合中。每个文档可以有完全不同的结构。
文档数据库包括CouchDB和MongoDB。
**
宽列数据库**
在列数据库中,我们有列族,而不是表,列族是行的容器。与关系数据库不同,我们不需要预先知道所有的列,每一行也不需要有相同数量的列。
列状数据库最适合分析大型数据集,包括Cassandra和HBase。
**
图形数据库**
这些数据库用于存储在图中最能表示关系的数据。数据保存在带有节点(实体)、属性(关于实体的信息)和线(实体之间的连接)的图结构中。
图形数据库的例子包括Neo4J和InfiniteGraph。
**
Blob数据库**
Blob更像是文件的键/值存储,可以通过api访问,如Amazon S3、Windows Azure Blob存储、谷歌云存储、Rackspace云文件或OpenStack Swift。
如何选择使用哪个数据库?
说到数据库技术,没有万能的解决方案。这就是为什么许多企业同时依赖SQL和NosQL数据库来满足不同的需求。
看看我下面的指导!
水平扩展web层
我们已经扩展了数据层,现在我们需要扩展web层。要做到这一点,我们需要将用户会话(状态)的数据从web层存储到一个数据库中,如关系数据库或NoSQL。它也被称为无状态架构。
不要使用有状态的架构。
我们必须尽可能地选择无状态架构,因为状态的实现限制了可伸缩性,降低了可用性,并增加了成本。
在上述场景中,负载均衡器可以实现最大效率,因为它可以为了最佳的请求处理选择任何服务器。
进阶概念
缓存
负载平衡帮助您在不断增加的服务器上进行水平扩展,但是缓存将使您能够更好地利用已经拥有的资源,以便在随后的请求中更快地返回数据。
如果数据不在缓存中,从数据库获取数据,然后将其保存到缓存中,随后即可在缓存中读取。
通过向服务器添加缓存,我们可以避免直接从数据库中读取网页或数据,从而减少服务器上的响应时间和负载。这有助于使我们的应用程序更具可伸缩性。
缓存可以应用于许多层,如数据库层、web服务器层和网络层。
内容分发网络 (CDN)
CDN服务器保存内容(如图像、网页等)的缓存副本,并从距离用户最近的位置提供服务。
CDN的使用缩小了用户页面加的载时间,因为数据是在离用户最近的位置被检索。这也有助于增加内容的可用性,因为它存储在多个位置。
CDN服务器向我们的Web服务器发出请求,以验证正在缓存的内容,并在需要时更新它们。缓存的内容通常是静态的,如HTML页面、图像、JavaScript文件、CSS文件等。
面向全球提供服务
当你的应用走向全球时,你将拥有并运营世界各地的数据中心,以确保你的产品每周7天、每天24小时运行。传入的请求将基于GeoDNS路由到“最佳”数据中心。
GeoDNS是一种DNS服务,可以根据客户所在的位置将域名解析为IP地址。从亚洲连接的客户端可能会获得与从欧洲连接的客户端不同的IP地址。
将所有方案整合到一起
迭代地应用所有这些技术,我们可以轻松地将系统扩展到超过1亿用户,如无状态架构,应用负载均衡器,尽可能多地使用缓存数据,支持多个数据中心,在CDN上托管静态资产,通过分片扩展您的数据层。等。
扩展架构设计是一个迭代的过程
参考
[3] https://www.oracle.com/database/
[5] https://en.wikipedia.org/wiki/Domain_Name_System
[6] https://www.facebook.com/note.php?note_id=10150468255628920