2. 大型互联网架构设计的四大原则(二)
2.2 大型互联网架构高伸缩设计
要学习伸缩性架构,就必须先掌握垂直扩展和水平扩展两个概念。
- 垂直扩展:是指通过增加服务器的配置来满足业务要求,可以增加CPU核数、内存大小、磁盘容量,或者更换读写更快的内存、网卡、固态硬盘。
- 水平扩展:是指通过增加服务器的数量来满足业务要求,服务器的配置可以不高,1台不够用2台,2台不够用4台。
垂直扩展和水平扩展就好像人搬桌子一样,垂直扩展是一个人搬不动则找一个更强壮的人来搬,甚至换成机器来搬;水平扩展是一个人搬不动则找两个人来搬,如果还搬不动则找十个人来搬。
高伸缩性主要是指服务的水平扩展能力,能够快速地通过增加服务器的数量提高系统性能和存储能力,从而能够支撑高并发请求,或者海量数据的存储。具有高伸缩性能够达到按需扩容和弹性伸缩的目的,对服务器资源的利用更加合理。
水平扩展需要借助负载均衡技术(参见2.1.3小节),然而应用想要支持水平扩展,最好提供无状态服务,有状态服务是难以水平扩展的。
无状态服务与有状态服务是相互对应的,主要看用户的多次请求是否存在上下文关系
和依赖关系
。
有状态服务修改为无状态服务主要有两种方案:数据同步
和数据共享
。
2.2.1 文件存储伸缩设计
伸缩性还体现在文件存储方面,对于互联网业务(如云相册、云存储、短视频、影音等),需要海量的文件存储空间,因为有大量的文件上传、下载操作。一方面要保证有足够的存储空间,能够随时扩容;另一方面还需要具有极高的访问效率和安全性,避免用户的文件丢失。
主要使用网络文件系统和分布式文件系统架构来解决这个问题,如NFS、GFS、FastDFS、HDFS、Ceph、GridFS、MogileFS、TFS、对象存储云服务等。
下面选择比较有代表性的NFS、GFS和FastDFS进行讲解。
1.NFS
NFS(Network File System,网络文件系统)能让服务器之间通过TCP/IP协议共享存储资源,可以将本地目录挂载到远程目录上,客户端可以透明地操作远程NFS服务器上的文件(读写远程文件与读写本地文件是一样的,应用程序无须任何修改)。
客户端可以在挂载点(/data目录)上进行创建、复制、重命名、移动、删除等各种文件操作,就好像在操作本地文件一样,所有操作均会同步到NFS服务器上执行。为了保证NFS服务的数据不丢失,还可以配置备份节点进行数据备份。
NFS的优点是对开发人员、运维人员透明,搭建和上手难度较低,易于维护。
NFS的缺点是:
- (1)存在单节点故障,NFS服务器宕机,就会导致所有客户端无法读取历史文件,并且会将新文件写入本地磁盘下,重新挂载需要手动同步;
- (2)NFS在高并发场景下性能存在瓶颈;
- (3)没有安全认证机制,数据都是通过明文传输,对数据完整性不做校验。因此,建议NFS在内网环境下中小规模系统使用。
2. GFS
GFS(Google File System,谷歌文件系统)采用一主多从的结构,由一个GFS master节点(主服务)和多个GFS chunkserver节点(块服务)构成。
GFS采用文件分块存储的方式,如图2-67所示。1个256MB的视频文件,可以被分为4个64MB的块文件,存储到GFS chunkserver节点中,为了保证文件的高可用,各个GFS chunkserver之间会进行相互复制(Replica),将自己节点上的块分散到其他节点上。文件被拆分后势必要记录它们的存储位置,在哪个GFS chunkserver节点的哪个块上存储,而这些位置信息及对应关系就是文件的元数据,被存储在GFS master节点中。
GFS master节点用于存储集群中的所有元数据信息,整个集群只有一个GFS master节点,并且数据全部存储在内存中,因此读写效率和一致性极高。 GFS master节点的内存容量也代表着GFS集群的最大存储容量。
GFS master节点以树形结构存储文件的命名空间(namespace),可以将这棵树理解为常见的目录结构或索引,目的就是记录文件的位置,以便快速地查找并读写文件。元数据包括文件与chunk的对应关系,以及chunk副本的存储位置。
GFS master和GFS chunkserver之间会保持心跳检查,从而判定存储节点的存活状态,以及数据存储现状,GFS master还负责GFS chunkserver的存储空间回收任务。
客户端要读取某个文件时的过程如下所示:
- (1)客户端去询问GFS master,文件存在哪里,从哪能找到它们?
- (2)GFS master根据文件名在元数据中进行检索,返回要读取内容的所有副本位置。
- (3)客户端直接与存储了该数据副本的GFS chunkserver交互,请求读取文件。
- (4)GFS chunkserver将数据返回给客户端。
通过文件的读取流程可以看出,客户端读取数据时,实际上直接与GFSchunkserver交互传输,并不会经过GFS master节点,以免GFS master成为性能瓶颈。
GFS的设计目标主要是针对大文件存储、读多写少的场景,支持弹性伸缩(增加GFS master的内存、增加GFS chunkserver节点的数量或存储容量即可)、高并发访问、数据冗余备份,能够运行在廉价低端的服务器上。
3. FastDFS
FastDFS(Fast Distribute File System,Fast分布式文件系统)是一个轻量级分布式文件系统,使用C语言编写,主要面向互联网业务。目前包括阿里巴巴、迅雷、58同城等大型互联网厂商均有使用。FastDFS具有冗余备份、负载均衡、伸缩扩容、副本容错、可用性高等特点,能够提供高效的文件上传和下载服务。
与GFS不同,FastDFS更适合存储占用空间小但数量巨大的“碎文件”,它不对文件进行分块存储,避免了文件拆分与合并的开销。
FastDFS主要分为两个集群,即Tracker集群和Storage集群(与GFS架构十分相似,Tracker等价于GFS master,Storage等价于GFS chunkserver)
Tracker集群负责负载均衡和元数据存储,Storage集群负责文件存储和副本复制。Storage定时向Tracker上报自己的节点状态、剩余存储空间、文件复制等情况。
2.2.2 数据库伸缩设计
应用和磁盘存储需要水平伸缩来提高可靠性、服务能力、数据容量,数据库也同样如此。数据库的扩容主要有3个目的,一是容量的增加,二是读写性能的改善,三是连接数的增加。
一些企业内部系统,用户数量和业务规模有限,因此并不会产生巨大的数据量,这时数据库的存储和读写性能均不会成为瓶颈,没有扩容的需要,因此无须考虑伸缩性。
对于一些互联网系统,前后端应用可以通过CDN、缓存、负载、水平扩展等技术解决瓶颈问题,但是数据库成为最终的读写集中点,每天都在产生海量的数据和读写请求,因此成为系统瓶颈。
Redis、MongoDB等NoSQL数据库虽然支持数据分片,但是并不能取代关系型数据库,对于逻辑关系紧密、复杂的系统,必须借助关系型数据库良好的事务特性来支持。绝大多数公司依然主要采用关系型数据库,而将其他数据库作为辅助。
对于MySQL数据库,虽然理论上单表可以支撑上亿条数据,但是性能较差,增加索引、优化SQL语句已经于事无补。阿里巴巴规约提出单表行数超过500万行或单表容量超过2GB,才推荐进行分库分表。这个建议的目的是避免过度设计。
数据库的扩容主要采用分库分表的策略,按照垂直拆分和水平拆分两个维度,可分为4种策略,分别是垂直分表、垂直分库、水平分表和水平分库。
1.垂直分表
垂直分表的特点是在一个数据库内,将一个表垂直拆分为多个关联表,表结构彼此不相同。
2.垂直分库
垂直分库的特点是将一个数据库拆分为多个数据库。
企业早期将所有业务表都放在一个数据库中,包含用户、订单、产品等所有数据。各个服务都连接到同一个库,从而造成数据库的存储、连接数、读写性能都成为系统瓶颈。
垂直分库就是将一系列相关的表单独拆分为一个数据库,达到专库专用的目的,可以提升数据库的读写性能、连接数。
垂直分库会对整个系统架构产生影响,并且需要考虑分布式事务问题,需要对业务进行更加细致的设计,开发成本较高。
虽然垂直分表、垂直分库有诸多好处,但是依然没有解决单表数据量过大的问题,每个表中还是存储全量数据。例如,一个订单表有一千万条数据,垂直拆分为多个表或多个库后,单表依然是一千万条数据。
3.水平分表
水平分表的特点是在同一个数据库内,将一个表水平拆分为多个表,表结构均相同。
当单表记录数过大时可以进行水平拆分,如图2-76所示,将商品表拆分为多张表,每张表中都只存储部分产品数据。对于每一条商品数据如何存储到表中,存储到哪个表,就涉及分片字段和分片算法的选取,一般使用主键作为分片字段,使用基于哈希取模和范围的分片算法。
- (1)基于哈希取模的分片算法:采用商品ID哈希取模的方式进行存储。基于哈希取模的分片算法的优点是可以让数据分布得更加均匀,但是数据存储比较分散,不适合范围类查询。
- (2)基于范围的分片算法:也可以基于分片字段的范围进行存储。
常用的还有基于时间范围的分片算法。例如,从2001年至2020年,每个年份创建一张表,这样将相同业务日期的数据存入相同的表中,既便于查询又方便转储和清理。
除了基于哈希取模和范围的分片算法,应用程序也可以自定义算法和路由规则。例如,可以定义一个路由规则表,每次进行增删改查时按照规则定义计算。
4.水平分库
水平分库的特点是将同一个表水平拆分到多个数据库中,每个表的结构相同。
水平分库与水平分表的原理基本相同,即将一个表拆分为多个相同结构的表,分散在不同的数据库中。
水平分库也面临着诸多问题:如何进行跨库关联查询?如何进行跨库的分页和排序?如何保证数据事务的一致性?如何进行数据迁移?分库分表的基本原则有以下3点。
- (1)不进行过度设计,不需要将所有库、所有表都进行分库分表。分库分表虽然有诸多好处,很好地解决了数据库的存储容量和性能瓶颈,但是程序复杂度会急剧升高,出现问题的概率呈指数级增长。
- (2)不在项目初期就进行分库分表,而是动态调整。当单表数据量增长到一定程度时再进行分库分表即可。
- (3)先垂直拆分,再水平拆分。如果垂直拆分能够解决问题,则不需要再进行水平拆分。
5.分库分表框架推荐
这里推荐使用Apache ShardingSphere框架,其起源于当当网开源的Sharding-JDBC框架,已于2020年4月16日成为 Apache 软件基金会的顶级项目。
以下内容来源于Apache ShardingSphere项目官网介绍。
Apache ShardingSphere 是一套开源的分布式数据库解决方案组成的生态圈,它由 JDBC、Proxy和 Sidecar(规划中)这3款既能够独立部署,又支持混合部署配合使用的产品组成。 它们均提供标准化的数据水平扩展、分布式事务和分布式治理等功能,可适用于如 Java 同构、异构语言、云原生等各种多样化的应用场景。
Apache ShardingSphere旨在充分合理地在分布式的场景下利用关系型数据库的计算和存储能力,而并非实现一个全新的关系型数据库。 关系型数据库当今依然占有巨大市场份额,是企业核心系统的基石,未来也难以撼动,我们更加注重在原有基础上提供增量,而非颠覆。
Apache ShardingSphere 5.x 版本开始致力于可插拔架构,项目的功能组件能够灵活地以可插拔的方式进行扩展。 目前,数据分片、读写分离、数据加密、影子库压测等功能,以及 MySQL、PostgreSQL、SQL Server、Oracle等 SQL 与协议的支持,均通过插件的方式植入项目。开发者能够像使用积木一样定制属于自己的独特系统。Apache ShardingSphere 目前已提供数十个SPI 作为系统的扩展点,仍在不断增加中。