欢迎您光临本小站。希望您在这里可以找到自己想要的信息。。。

大规模分布式存储系统原理与架构(四)

架构&设计模式 water 2469℃ 0评论

分布式系统:

木桶效应,架构设计之初要求我们能够估算系统的性能从而权衡不同的设计方法

分布式系统面临的第一个问题就是数据分布,即将数据均匀地分不到多个存储节点。另外,为了保证可靠性和可用性,需要将数据复制多个副本,这就带来了多个副本之间的数据一致性问题。大规模分布式存储系统的重要目标就是节省成本,因而只能采用性价比较高的PC服务器。这些服务器性能很好,但故障率很高,要求系统能够在软件层面实现自动容错。

分布式系统中有两个重要的协议,包括Paxos选举协议以及两阶段提交协议。Paxos协议用于多个节点之间达成一致,往往用于实现总控节点选举。两阶段提交协议用于保证跨多个节点操作的原子性,这些操作要么全部成功,要么全部失败。

基本概念

异常:服务器节点是不可靠的,网络也是不可靠的。

异常类型:

1. 服务器宕机,原因可能是内存错误、服务器停电,称为“不可用”。服务器重启后,节点将失去所有的内存信息。因此,设计存储系统时需要考虑如何通过读取持久化介质(如机械硬盘,固态硬盘)中的数据来恢复内存信息,从而恢复到宕机前的某个一致的状态。进程运行过程中也可能随时因为core dump等原因退出,和服务器宕机一样,进程重启后也需要恢复内存信息

2. 网路异常:原因可能是消息丢失、消息乱序或者网络包数据错误。有一种特殊的网络异常称为“网络分区”。设计容错系统的一个基本原则是:网络永远是不可靠的,任何一个消息只有收到对方的回复才可以认为发送成功,系统设计时总是假设网络将会出现异常并采取相应的处理措施。

3. 磁盘故障:分为两种情况:磁盘损坏和磁盘数据错误。磁盘损坏,需要考虑将数据存储到多台服务器。对于磁盘数据错误,往往可以采用校验和(checksum)机制来解决,这样的机制即可以在操作系统层面实现,又可以在上层的分布式存储系统层面实现

超时:由于网络异常的存在,分布式存储系统中请求结果存在“三态”的概念。在单机系统中,只要服务器没有发生异常,每个函数的执行结果是确定的,要么成功,要么失败。然而,在分布式系统中,如果是某个节点向另外一个节点发起RPC调用,这个RPC执行的结果有三种状态:“成功”、“失败”、“超时”,也称为分布式存储系统的三态

当出现超时状态时,只能通过不断读取之前操作的状态来验证RPC操作是否成功。当然,设计分布式存储系统时可以将操作设计为“幂等”的,也就是说,操作执行一次与执行多次的结果相同。

一致性

异常的存在,分布式存储系统设计时往往将数据冗余存储多份,每一份称为一个副本。这样,当某个节点出现故障时,可以从其他副本上读到数据。可以这么认为,副本是分布式存储系统容错技术的唯一手段。由于多个副本的存在,如何保证副本之间的一致性是整个分布式系统的理论核心

两个角度理解一致性:第一个角度是用户,或者说是客户端,即客户端读写操作是否符合某种特性;第二个角度死存储系统,即存储系统的多个副本之间是否一致,更新的顺序是否相同。

首先定义如下场景,这个场景包含三个组成部分:

存储系统:存储系统可以理解为一个黑盒子,它为我们提供了可用性和持久性的保证。

客户端A:客户端A主要实现从存储系统write和read操作

客户端B和客户端C: 客户端B和C是独立于A,并且B和C也相互独立的,它们同时也实现对存储系统的write和read操作

从客户端的角度来看,一致性包含如下三种情况:

强一致性:假如A写入了一个值到存储系统,存储系统保证后续A,B,C的读取操作都将返回最新值。当然,如果写入操作“超时”,那么成功或者失败都是可能的,客户端A不应该做任何假设

弱一致性:假如A先写入了一个值到存储系统,存储系统不能保证后续A,B,C的读取操作是否能够读取到最新值。

最终一致性:最终一致性是弱一致性的一种特例。加入A首先写入一个值到存储系统,存储系统保证如果后续没有写操作更新同样的值,A,B,C的读取操作“最终”都会读取到A写入的新值。“最终”一致性有一个“不一致窗口”的概念,它特指从A写入值,到后续A,B,C读取到值的这段时间。“不一致性窗口”的大小依赖于一下的几个因素:交互延迟,系统负载,以及复制协议要求同步的副本数。

最终一致性描述比较粗略。其他常见的变体如下:

读写一致性:如果客户端A写入了最新的值,那么A的后续操作都会读取到最新值。但是其他用户(比如B或者C)可能要过一会才能看到

会话一致性:要求客户端和存储系统交互的整个会话期间保证读写一致性。如果原有会话因为某种原因失效而创建了新的会话,原有会话和新会话之间的操作不保证读写一致性。

单调读一致性:如果客户端A已经读取了对象的某个值,那么后续操作将不会读取到更早的值

单调写一致性:客户端A的写操作按顺序完成,这就意味着,对于同一个客户端的操作,存储系统的多个副本需要按照与客户端相同的顺序完成

从存储系统的角度看,一致性主要包含如下几个方面:

副本一致性:存储系统的多个副本之间的数据是否一致,不一致的时间窗口等

更新顺序一致性:存储系统的多个副本之间是否按照相同的顺序执行更新操作

一般来说,存储系统可以支持强一致性,也可以为了性能考虑只支持最终一致性。从客户端的角度看,一般要求存储系统能够支持读写一致性,会话一致性,单调读,单调写等特性。

衡量指标

性能:系统的吞吐能力以及系统的响应时间。其中,系统的吞吐能力指系统在某一段时间可以处理的请求总数,通常用每秒处理的读操作树(QPS,Query Per Second)或者写操作数(TPS, Transaction Per Second)来衡量;系统的响应延迟,指从某个请求发出到接收到返回结果消耗的时间,通常用平均延时或者99.9%以上请求的最大延时来衡量。这两个指标往往是矛盾的,最求高吞吐的系统,往往很难做的低延迟;最求低延迟的系统,吞吐量也会收到限制。因此,设计系统时需要衡量这两个指标。

可用性:是指系统在面对各种异常时可以提供正常服务的能力。系统的可用性可以用系统停服务的时间与正常服务的时间的比例来衡量,例如某系统的可用性为4个9(99.99%)。系统可用性往往体现了系统的整体代码质量以及容错能力

一致性:一般来说,越是强的一致性模型,用户使用起来越简单,系统部署在同一个数据中心,只要系统设计合理,在保证强一致性的前提下,不会对性能和可用性造成太大的影响

可扩展性:指分布式存储系统通过扩展集群服务规模来提高系统存储容量,计算量和性能的能力。理想的分布式存储系统实现了“线性可扩展”,也就是随着集群规模的增加,系统的整体性能与服务器数量呈线性关系

性能分析:

给定一个问题,往往会有多种设计方案,而方案评估的一个重要指标就是性能,如何在系统设计之初估算存储系统的性能是存储工程师的必备技能。性能分析用来判断设计方案是否存在瓶颈点,权衡多种设计方案,另外,性能分析也可作为后续性能优化的依据。性能分析与性能优化是相对的,系统设计之初通过性能分析确定设计目标,防止出现重大的设计失误,等到系统试运行后,需要通过性能优化方法找出性能中的瓶颈点并逐步消除,使得系统达到设计之初确定的设计目标

性能分析的结果是不准确的,然而,至少可以保证,估算的结果与实际值不会相差一个数量级。设计之初首先分析整体架构,接着重点分析可能成为瓶颈的单机模块。系统中的资源(CPU、内存、磁盘、网络)是有限的,性能分析就是需要找出可能出现的资源瓶颈。

数据分布

分布式系统区别于传统单机系统在于能够将数据分不到多个节点,并在多个节点之间实现负载均衡。

分布式存储系统需要能够自动识别负载高的节点,当某台机器的负载较高时,将它服务的部分数据迁移到其他机器,实现自动负载均衡

分布式存储系统的一个基本要求就是透明性,包括数据分布透明性,数据迁移透明,数据复制透明性,故障处理透明性

哈希分布

找出一个散列特性很好的哈希函数是很难的。这是因为,如果按照主键散列,那么同一个用户id下的数据可能被分散到多台服务器,这会使得一次操作同一个用户id下的多条记录变得困难;如果按照用户id散列,容易出现“数据倾斜(data skew)”问题,即某些大用户的数据量很大,无论集群的规模有多大,这些用户始终由一台服务器处理。

处理大用户问题一般有两种方式,一种是手动拆分,即线下标记系统中的大用户,并根据这些大用户的数据量将其拆分到多台服务器上。这就相当于在哈希分布的基础上针对这些大用户特殊处理;另一种方式是自动拆分,即数据分布算法能够动态调整,自动将大用户的数据拆分到多台服务器上。

传统的哈希分布算法还有一个问题:当服务器上线或者下线时,N值发生变化,数据映射完全被打乱,几乎所有的数据都需要重新分布,这种带来大量的数据迁移

一种思路是不在简单的将哈希值和服务器个数做除法取模映射,而是将哈希值与服务器的对应关系作为元数据,交给专门的元数据服务器来管理。访问数据时,首先计算哈希值,再查询元数据服务器,获得该哈希值对应的服务器。这样,集群扩容时,可以将部分哈希值分配给新加入的机器并迁移对应的数据

另一种思路就是采用一致性哈希算法。可以看出,一致性哈希算法在很大程度上避免了数据迁移。为了查找集群中的服务器,需要维护每台机器在哈希环中的位置信息。常见做法:

1. 每台服务器记录它的前一个以及后一个节点的位置信息

2. 每台服务器维护一个大小为n的路由表

3. 每台服务器维护整个集群中所有服务器的位置信息,这样的P2P系统在每个服务器节点上都维护所有服务器的位置信息,而带有总控节点的存储系统往往由总控节点统一维护

一致性哈希还需要考虑负载均衡,增加服务节点后,虽然只影响后继节点,但需要迁移的数据过多,整个集群的负载不均衡。一种自然的想法是将需要迁移的数据分散到整个集群。

顺序分布:哈希散列破坏了数据的有序性,只支持随机读取操作,不能够支持顺序扫描。顺序分布在分布式表格系统中比较常见,一般做法是将大表顺序划分为连续的范围,每个范围称为一个子表,总控服务器负责将这些子表按照一定的策略分配到存储节点上。Meta表是可选的,某些系统只有根表(Root表)一级索引,在Root表中维护用户表的位置信息,即每个User子表在那个存储节点上。为了支持更大的集群规模,Bigtable这样的系统将索引分为两级:根表以及元数据表(Meta表),由Meta表维护User表的位置信息,而Root表用来维护Meta表的位置信息。读User表时,需要通过Meta表查找相应的User子表所在的存储节点。而读取Meta表又需要通过Root表查找相应的Meta子表所在的存储节点。

负载均衡

分布式存储系统的每个集群中一般有一个总控节点,其他节点为工作节点,有总控节点根据全局负责信息进行整体调整。工作节点刚上线时,总控节点需要将数据迁移到该节点,另外,系统进行过程中也需要不断的执行迁移任务,将数据从负载高的工作节点迁移到负载较低的工作节点

工作节点通过心跳包(Heartbeat,定时发送)将节点负载相关的信息发送给主控节点。主控节点计算出工作节点的负责以及需要迁移的数据,生成迁移任务放入迁移队列中等待执行。负载均衡操作需要控制节奏

迁移备副本不会对服务造成影响,迁移主副本也可以首先将数据的读写服务切换到其他备份副本。

复制

分布式存储系统通过复制协议将数据同步到多个存储节点,并确保多个副本之间的数据一致性。复制协议分为两种,强同步复制以及异步复制,二者的区别在于用户的写请求是否需要同步到备副本才可以返回成功。

一致性和可用性是矛盾的,强同步复制协议可以保证主备副本之间的一致性,但是但备副本出现故障时,也可能阻塞存储系统的正常写服务,系统的整体可用性受到影响;异步复制协议的可用性比较好,但是一致性得不到保障,主副本出现故障时还有数据丢失的可能。

强同步复制和异步复制都是将主副本的数据以某种形式发送到其他副本,这种复制协议称为基于主副本的复制协议。选举主副本,一般用Paxos协议

主备副本之间的复制一般通过操作日志来实现

除了基于主副本的复制协议,比如NWR复制协议,N为副本数量,W为写操作的副本数,R为读操作的副本数。只要W+R>N,可以保证读到的副本中至少有一个包含了最新的更新。问题,在于不同副本的操作顺序可能不一致

一致性与可用性

CAP理论:一致性(Consistency)、可用性(Availability)以及分区可容忍性(Toleance of network Partition)

存储系统设计时需要在一致性和可用性之间权衡。比如Oracle数据库的DataGuard复制组件包含三种模式

最大保护模式:同步复制模式,写操作要求主控先将操作日志同步到至少一个备库才可以返回客户端成功

最大性能模式:异步复制模式

最大可用模式:正常情况下相当于最大保护模式当网络出现故障,切换为最大性能模式

容错

故障检查往往通过租约(Lease)协议实现。接着,需要能够将服务复制或者迁移到集群中的其他正常服务器的存储节点

单机故障和磁盘故障发生概率最高,单台服务器故障,一般来说,分布式存储系统会保存多份数据,机架发生的概率相对也是比较高,需要避免将数据的所有副本都分布在同一个机架。

心跳包,机器A和机器B之间需要对“机器B是否应该被认为发生故障且停止服务”达成一致。异构网络无法达成一致,所以采用租约模式

租约机制时需要考虑一个提前量

故障恢复

当总控机检测到工作机发生故障时,需要将服务迁移到其他工作机节点。常见的分布式存储系统分为两种结构:单层结构和双层结构。大部分系统为单层结构

节点下线分为两种情况:一种是临时故障,节点过一段时间将重新上线;另一种情况是永久性故障,比如磁盘损坏。总控节点一般需要等待一段时间,如果发生永久性故障,需要执行增加副本操作

两层结构的分布式存储系统会将所有的数据持久化写入底层的分布式文件系统,每个数据分片同一时刻只有一个提供服务的节点

节点故障会影响系统服务,在故障检测以及故障恢复的过程中,不能提供写服务及强一致性服务。停服务时间包含两个部分,故障检测时间以及故障恢复时间。单层结构的备副本和主副本之间保持时间同步,切换为主副本的时间很短;两层结构故障恢复往往实现成只需要将数据的索引,而不是所有的数据,加载到内存中

总控节点自身也可能出现故障,为实现总控节点的高可用性(High Availability),总控节点的状态也将实时同步到备机。为了进行选主或者维护系统中重要的全局信息,可以维护一套通过Paxos协议实现的分布式服务,比如Google Chubby或者它的开源实现Apache Zookeeper

可扩展性

通过数据分布,复制以及容错等机制,能够将分布式存储系统部署到成千上万台服务器。可扩展性的实现手段很多,如通过增加副本个数或缓存提高读取能力,将数据分片使得每个分片可以被分配到不同的工作节点以实现分布式处理,把数据复制到多个数据中心。

分布式存储系统大多都带有总控节点,很多人会自然地联想到总控节点的瓶颈问题,认为P2P架构更为优势。然而,事实却并非如此,主流的分布式存储系统大多带有总控节点。且能够支持成千上万台的集群规模

另外,传统的数据库也能通过分开分表等方式对系统进行水平扩展,当系统处理能力不足时,可以通过增加存储节点来扩容

如何衡量分布式存储系统的可扩展性,应该综合考虑节点故障后的恢复时间,扩容的自动化程度,扩容的灵活性。

总控节点

分布式存储系统中往往有一个总控节点用于维护数据分布信息,执行工作机管理,数据定位,故障检测和恢复,负载均衡等全局调度工作。通过引入总控节点,可以使得系统的设计更加简单。。并且更加容易做到强一致性,对用户友好。

如果总控节点称为瓶颈,可以采用两级结构,在总控机与工作机之间增加一层元数据节点,每个元数据节点只维护一部分而不是整个分布式文件系统的元数据。这样总控机也只需要维护元数据节点的元数据,不可能成为性能瓶颈。虽然看似增加了一次网络请求,但是客户端总是能够缓存总控机上的元数据,因此并不会带来额外开销

数据库扩容

通过主从复制提高系统的读取能力,通过垂直拆分和水平拆分将数据分不到多个存储节点,通过主从复制将系统扩展到多个数据中心。

先按照业务将表垂直拆分到不同DB中,再将每张表通过哈希的方式水平拆分到不同的存储节点上。

异构系统

大规模分布式存储系统要求具有线性扩展性,即随时加入或者删除一个或者多个存储节点,系统的处理能力与存储节点的个数成线性关系。为了实现线性可扩展性,存储系统的存储节点之间是异构的。异构系统将数据划分为很多大小接近的分片,每个分片的多个副本可以分不到集群中的任何一个存储节点。如果某个节点发生故障,原有的服务将由整个集群而不是某几个固定的存储节点来恢复

分布式协议

分布式系统涉及的协议很多,例如租约,复制协议,一致性协议,其中以两阶段提交协议和Paxos协议最具有代表性。两阶段提交协议用于保证跨多个节点操作的原子性,也就是说,跨多个节点的操作要么在所有节点上全部执行成功,要么全部失败。

Paxos协议用于确保多个节点对某个投票达成一致。

两阶段提交协议

(Tow-phase Commit, 2PC)经常用来实现分布式事务,在两阶段协议中,系统一般包含两类节点:一类为协调者,通常一个系统中只有一个;另一类为事务参与者,一般包含多个。

分为两个阶段:请求阶段、提交阶段

Paxos协议

考虑到主节点可能出现故障,系统需要选举出新的主节点,就能够在这些节点上构建高可用的全局服务,例如分布式锁服务,全局命名和配置服务等。

Paxos与2PC

Paxos协议和2PC协议在分布式系统中所起的作用并不相同。Paxos协议用于保证同一个数据分片的多个副本之间的数据一致性。当这些副本分不到不同的数据中心时,这个需求尤其强烈。2PC协议用于保证属于多个数据分片上的操作的原子性。

Paxos协议有两种用法:一种用法是用它来实现全局的锁服务或者命名和配置服务,例如Google Chubby以及Apache Zookeeper。另外一种用法是用它来将用户数据复制到多个数据中心,例如Google Megastore以及Google Spanner

因此,常见的做法是将2PC和Paxos协议结合起来,通过2PC保证多个数据分片上的操作的原子性,通过Paxos协议实现同一个数据分片的多个副本之间的一致性。另外,通过Paxos协议解决2PC协议中协调者宕机问题。

跨机房部署

在分布式系统中,跨机房问题一直都是老大难问题。机房之间的网络延时较大,且不稳定。跨机房问题主要包含两个方面:数据同步以及服务切换。跨机房部署方案有三个:集群整体切换、单个集群跨机房、Paxos选主副本。

转载请注明:学时网 » 大规模分布式存储系统原理与架构(四)

喜欢 (0)or分享 (0)

您必须 登录 才能发表评论!