跳至主要內容

分布式基础理论

钝悟...大约 18 分钟分布式分布式理论分布式理论指标挑战超时检测NTP逻辑时钟

分布式基础理论

分布式系统的发展

罗马不是一天建成的,同理,现代分布式系统架构也不是一蹴而就的,而是逐步发展的演化过程。随着业务的不断发展,用户体量的增加,系统的复杂度势必不断攀升,最终迫使系统架构进化,以应对挑战。

了解分布式系统架构的演化过程,有利于我们了解架构进化的发展规律和业界一些成熟的应对方案。帮助我们在实际工作中,如何去思考架构,如何去凝练解决方案。

单机架构

  • 场景:网站运营初期,访问用户少,一台服务器绰绰有余。
  • 特征应用程序、数据库、文件等所有的资源都在一台服务器上。
  • 描述:通常服务器操作系统使用 linux,应用程序使用 PHP 开发,然后部署在 Apache 上,数据库使用 Mysql,通俗称为 LAMP。汇集各种免费开源软件以及一台廉价服务器就可以开始系统的发展之路了。

应用服务和数据服务分离

  • 场景:越来越多的用户访问导致性能越来越差,越来越多的数据导致存储空间不足,一台服务器已不足以支撑。
  • 特征应用服务器、数据库服务器、文件服务器分别独立部署。
  • 描述:三台服务器对性能要求各不相同:
    • 应用服务器要处理大量业务逻辑,因此需要更快更强大的 CPU;
    • 数据库服务器需要快速磁盘检索和数据缓存,因此需要更快的硬盘和更大的内存;
    • 文件服务器需要存储大量文件,因此需要更大容量的硬盘。

使用缓存改善性能

  • 场景:随着用户逐渐增多,数据库压力太大导致访问延迟。
  • 特征:由于网站访问和财富分配一样遵循二八定律:80% 的业务访问集中在 20% 的数据上将数据库中访问较集中的少部分数据缓存在内存中,可以减少数据库的访问次数,降低数据库的访问压力。
  • 描述:缓存分为两种:应用服务器上的本地缓存和分布式缓存服务器上的远程缓存。
    • 本地缓存访问速度更快,但缓存数据量有限,同时存在与应用程序争用内存的情况。
    • 分布式缓存可以采用集群方式,理论上可以做到不受内存容量限制的缓存服务。

负载均衡

  • 场景:使用缓存后,数据库访问压力得到有效缓解。但是单一应用服务器能够处理的请求连接有限,在访问高峰期,成为瓶颈。
  • 特征多台服务器通过负载均衡同时向外部提供服务,解决单一服务器处理能力和存储空间不足的问题。
  • 描述:使用集群是系统解决高并发、海量数据问题的常用手段。通过向集群中追加资源,提升系统的并发处理能力,使得服务器的负载压力不再成为整个系统的瓶颈。

数据库读写分离

  • 场景:网站使用缓存后,使绝大部分数据读操作访问都可以不通过数据库就能完成,但是仍有一部分读操作和全部的写操作需要访问数据库,在网站的用户达到一定规模后,数据库因为负载压力过高而成为网站的瓶颈。
  • 特征:目前大部分的主流数据库都提供主从热备功能,通过配置两台数据库主从关系,可以将一台数据库服务器的数据更新同步到一台服务器上。网站利用数据库的主从热备功能,实现数据库读写分离,从而改善数据库负载压力。
  • 描述:应用服务器在写操作的时候,访问主数据库,主数据库通过主从复制机制将数据更新同步到从数据库。这样当应用服务器在读操作的时候,访问从数据库获得数据。为了便于应用程序访问读写分离后的数据库,通常在应用服务器端使用专门的数据访问模块,使数据库读写分离的对应用透明。

多级缓存

  • 场景:中国网络环境复杂,不同地区的用户访问网站时,速度差别也极大。
  • 特征采用 CDN 和反向代理加快系统的静态资源访问速度。
  • 描述:CDN 和反向代理的基本原理都是缓存,区别在于:
    • CDN 部署在网络提供商的机房,使用户在请求网站服务时,可以从距离自己最近的网络提供商机房获取数据;
    • 而反向代理则部署在网站的中心机房,当用户请求到达中心机房后,首先访问的服务器时反向代理服务器,如果反向代理服务器中缓存着用户请求的资源,就将其直接返回给用户。

业务拆分

  • 场景:大型网站的业务场景日益复杂,分为多个产品线。
  • 特征:采用分而治之的手段将整个网站业务分成不同的产品线。系统上按照业务进行拆分改造,应用服务器按照业务区分进行分别部署。
  • 描述:应用之间可以通过超链接建立关系,也可以通过消息队列进行数据分发,当然更多的还是通过访问同一个数据存储系统来构成一个关联的完整系统。
    • 纵向拆分将一个大应用拆分为多个小应用,如果新业务较为独立,那么就直接将其设计部署为一个独立的 Web 应用系统。纵向拆分相对较为简单,通过梳理业务,将较少相关的业务剥离即可。
    • 横向拆分将复用的业务拆分出来,独立部署为分布式服务,新增业务只需要调用这些分布式服务横向拆分需要识别可复用的业务,设计服务接口,规范服务依赖关系。

分库分表

  • 场景:随着大型网站业务持续增长,数据库经过读写分离,从一台服务器拆分为两台服务器,依然不能满足需求。
  • 特征数据库采用分布式数据库。
  • 描述:分布式数据库是数据库拆分的最后方法,只有在单表数据规模非常庞大的时候才使用。不到不得已时,更常用的数据库拆分手段是业务分库,将不同的业务数据库部署在不同的物理服务器上。

分布式组件

  • 场景:随着网站业务越来越复杂,对数据存储和检索的需求也越来越复杂。
  • 特征系统引入 NoSQL 数据库及搜索引擎。
  • 描述:NoSQL 数据库及搜索引擎对可伸缩的分布式特性具有更好的支持。应用服务器通过统一数据访问模块访问各种数据,减轻应用程序管理诸多数据源的麻烦。

微服务

  • 场景:随着业务越拆越小,存储系统越来越庞大,应用系统整体复杂程度呈指数级上升,部署维护越来越困难。由于所有应用要和所有数据库系统连接,最终导致数据库连接资源不足,拒绝服务。
  • 特征公共业务提取出来,独立部署。由这些可复用的业务连接数据库,通过分布式服务提供共用业务服务。
  • 描述:大型网站的架构演化到这里,基本上大多数的技术问题都得以解决,诸如跨数据中心的实时数据同步和具体网站业务相关的问题也都可以组合改进现有技术架构来解决。

分布式系统的指标

分布式系统的目标是提升系统的整体性能和吞吐量,另外还要尽量保证分布式系统的容错性

由分布式系统的目标很容易得出分布式系统的关键指标:性能、可用性、可扩展性。这些指标,正对应着耳熟能详的分布式系统“三高”特性——高并发、高性能、高可用。

性能(Performance)

性能用于衡量一个系统处理各种任务的能力。

常见的性能指标有:

  • 吞吐量(Throughput) - 系统在一定时间内可以处理的任务数。常见的吞吐量指标有:
    • QPS - Queries Per Second 的缩写,即每秒查询数。
    • TPS - Transactions Per Second 的缩写,即每秒事务数。
  • 响应时间(Response Time) - 执行一个请求从开始到最后收到响应数据所花费的总体时间,即从客户端发起请求到收到服务器响应结果的时间。
  • 并发数(Concurrency) - 并发数是指系统能同时处理请求的数量,这个也反映了系统的负载能力。并发意味着可以同时进行多个处理。并发在现代编程中无处不在,网络中有多台计算机同时存在,一台计算机上同时运行着多个应用程序。

以上三个指标的关系大致为:

QPS(TPS)= 并发数 / 平均响应时间
并发数 = QPS(TPS) * 平均响应时间

可用性(Availability)

可用性:指的是系统在面对各种异常时可以正确提供服务的能力。

系统的可用性可以用系统停止服务的时间与总的时间之比衡量。

行业内一般用几个 9 表示可用性指标,对应用的可用性程度一般衡量标准有三个 9 到五个 9;一般我们的系统至少要到 4 个 9(99.99%)的可用性才能谈得上高可用。

可用性年故障时间
99.9999%32 秒
99.999%5 分 15 秒
99.99%52 分 34 秒
99.9%8 小时 46 分
99%3 天 15 小时 36 分

而所谓的高可用,就是:在任何情况下,让服务尽最大可能对外提供服务

可扩展性(Scalability)

**可扩展性(Scalability)**指的是分布式系统通过扩展集群机器规模提高系统性能 (吞吐、响应时间、 完成时间)、存储容量、计算能力的特性,是分布式系统的特有性质。

系统扩展可以分为垂直扩展、水平扩展。

  • 垂直扩展,即提升单机的硬件处理能力,比如 CPU 处理能力,内存容量,磁盘等方面。但是,单机是有性能瓶颈的,一旦触及瓶颈,再想提升,付出的成本和代价会极高。通俗来说,就三个字:得加钱
  • 水平扩展:采用分而治之的思想,通过集群来分担吞吐量。集群中的应用机器(节点)通常被设计成无状态,用户可以请求任何一个节点,这些节点共同分担访问压力。水平扩展有两个要点:
    • 集群化、分区化:将一个完整的应用化整为零,如果是无状态应用,可以直接集群化部署;如果是有状态应用,可以将状态数据分区(分片),然后部署到多台机器上。
    • 负载均衡:集群化、分区化后,要解决的问题是,请求应该被分发(寻址)到哪台机器上。这就需要通过某种策略来控制分发,这种技术就是负载均衡。

分布式系统的分类

分布式技术错综复杂、知识庞杂,且各种技术相互耦合,所以不容易划分层次。

从应用的维度来看,大致可以将分布式系统分为以下四类:

  • 分布式计算:解决应用的分布式计算问题。基于分布式计算模式,包括批处理计算、离线计算、在线计算、融合计算等,根据应用类型构建高效智能的分布式计算框架。
  • 分布式存储:解决数据的分布式和多元化问题。包括分布式数据库、分布式文件系统、分布式缓存等,支持不同类型的数据的存储和管理。
  • 分布式通信:解决进程间的分布式通信问题。通过消息队列、远程调用等方式,实现简单高效的通信。
  • 分布式资源管理:解决资源的分布式和异构性问题。将 CPU、内存、IO 等物理资源虚拟化,新城逻辑资源池,以便统一管理。

此外,分布式系统都需要面对一些共性问题,可以视为分布式系统技术的基石:

  • 分布式协同 - 解决分布式状态及数据一致性的问题。代表技术:分布式互斥、分布式共识、分布式选举、分布式选举等。
  • 分布式调度 - 解决分布式系统资源、请求分配调度的问题。代表技术:服务注册和发现、服务路由、负载均衡、流量控制等。
  • 分布式容错 - 解决分布式系统中故障分析、处理的问题,保证系统整体可靠性。代表技术:链路追踪、故障隔离、故障转移等。
  • 分布式部署 - 解决分布式系统部署问题。代表技术:CI/CD、容器化等。

分布式系统的挑战

当程序运行在单机上时,通常会以一种可预测的方式运行:要么正常,要么异常。

一旦程序运行在多台机器上时,面临的场景就会变得复杂而难以预料。在分布式系统中,系统的某些部分可能会出现不可预知的故障,这被称为部分失效(partial failure)。问题的难点就在于部分失效是不确定性的。你甚至不确定请求是否成功了,因为消息通过网络传播的时间也是不确定的!这种不确定性和部分失效的可能性,使得分布式系统难以工作。

扩展阅读:The Eight Fallacies of Distributed Computing - Tech Talkopen in new window 一文中提出了分布式系统新手常有的 8 种误区。

为什么我们要深刻地认识这 8 个错误?这是因为,我们需要清楚地认识到——在分布式系统中,故障是不可避免的。因此,如果要构建一个可靠的分布式系统,就必须要建立容错机制。很可能大部分组件在大部分时间都正常工作。然而,迟早会有一部分系统出现故障,软件必须以某种方式处理。故障处理必须是软件设计的一部分,并且作为软件的运维,你需要知道在发生故障的情况下,软件可能会表现出怎样的行为。

不可靠的网络

互联网以及大多数数据中心的内部网络(通常是以太网)都是异步网络。当通过网络发送数据包时,数据包可能会丢失或者延迟;同样,回复也可能会丢失或延迟。所以如果没有收到回复,并不能确定消息是否发送成功。传输的过程中,可能有各种各样的问题:

  1. 请求可能已经丢失(可能是被拔掉了网线)。
  2. 请求可能正在某个队列中等待,无法马上发送(可能是网络或接收方已经超负荷)。
  3. 远程接收节点可能已经失效(可能是崩愤或关机)。
  4. 远程节点可能暂时无法响应(例如正在运行长时间的垃圾回收)。
  5. 远程接收节点已经完成了请求处理,但回复却在网络中丢失(例如网络交换机配置错误)。
  6. 远程接收节点已经完成了请求处理,但回复却被延迟处理(例如网络或者发送者的机器过载)。
如果发送请求并没有得到响应,则无法区分(a)请求是否丢失,(b)远程节点是否关闭,或(c)响应是否丢失
如果发送请求并没有得到响应,则无法区分(a)请求是否丢失,(b)远程节点是否关闭,或(c)响应是否丢失

在大多数情况下,系统并没有准确判断节点是否发生故障的机制。因此,分布式系统中,一般通过超时检测来判断远程节点是否可用。但是,超时无法区分网络和节点故障,且可变的网络延迟有时会导致节点被误认为发生崩溃。

超时检测的一个关键点是超时大小的设置:

  • 超时时间如果设置过大,意味着等待时间更久,才能判定节点失效(在此期间,用户只能等待或拿到错误信息)。

  • 超时时间如果设置过小,虽然可以更快检测故障,但增加了误判的可能——节点可能实际上是活着的。当一个节点被宣告为失效,其承担的职责要交给到其他节点,这个过程会给其他节点以及网络带来额外负担,特别是如果此时系统已经处于高负荷状态。

对此,可以先设置一个经验值,然后通过实验逐步调整:先在多台机器上,多次测量往返时间,以确定延迟的大概范围;然后结合应用特点,在故障检测与过早超时风险之间选择一个合适的中间值。更好的做法是:持续测量响应时间及其变化(抖动),然后根据最新的响应时间分布来动态调整。

不可靠的时钟

时钟和计时非常重要。有许多应用程序以各种方式依赖于时钟,例如:

  1. 某个请求是否超时了?
  2. 某项服务的 99 %的响应时间是多少?
  3. 在过去的五分钟内,服务平均每秒处理多少个查询?
  4. 用户在我们的网站上浏览花了多段时间?
  5. 这篇文章什么时候发表?
  6. 在什么时间发送提醒邮件?
  7. 这个缓存条目何时过期?
  8. 日志文件中错误消息的时间戳是多少?

在分布式系统中,时间总是件棘手的问题,由于跨节点通信不可能即时完成,消息经由网络从一台机器到另一台机器总是需要花费时间。收到消息的时间应该晚于发送的时间,但是由于网络的不确定延迟,精确测量面临着很多挑战。这些情况使得多节点通信时很难确定事情发生的先后顺序。

为了保证每台机器的时间同步,最常用的机制是 网络时间协议(Network Time Protocol, NTP),它可以根据一组专门的时间服务器来调整本地时间。需要注意的是,即使使用了 NTP 进行时间同步,但是依然会存在一些误差:一方面受限于 NTP 本身的同步精度,此外还受限于网络通信的延迟。

如果想要保证时序,另一种方案是采用逻辑时钟。**逻辑时钟(logic clock)**是基于递增计数器,对于排序事件来说是更安全的选择。逻辑时钟仅测量事件的相对顺序(无论一个事件发生在另一个事件之前还是之后)。

参考资料

评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v2.15.7