写点什么

Week 06 学习总结

用户头像
卧石漾溪
关注
发布于: 2020 年 07 月 15 日

本周主要介绍了分布式数据库的分片及部署方案、CAP 原理、数据最终一致性、HBase 架构相关知识、ACID 与 BASE、ZooKeeper 实现分布一致性以及一个 Doris 海量 KV Engine 的案例。

 

1       分布式数据库(下)

1.1     数据分片概念

当单表数据量很大的时候,或者有较高并发写数据的情况下,就需要进行数据分片技术

了。

数据分片:

       把一张数据量很大的表拆分成若干“片”,分表存储在不同的服务器上。这样不管数据读还是写的时候,都只需访问对应的“片“所在的服务器即可,这样有效降低了单个服务器的负载压力,降低服务器数据存储的写和读的压力。

 

1.2     数据分片的实现

1.2.1     硬编码实现



1.2.2     映射表外部存储

可看作是对硬编码分片的一种优化方法,比硬编码更加灵活:



注意:

       这种方式实际中也不太常用,因为数据量很大的情况下,外部存储映射表记录数也会很大;另外原本进行一次查找就变成了两次查找了(先要查映射表);所以这种方案会增加复杂性以及可能导致数据不一致性。

 

1.2.3     分布式数据库中间件

前两种分片方案都需要应用程序来决定到底要访问哪台数据库服务器,这样其实会带来

应用程序的复杂性。而基于分布式数据库中间件的分片可以做到对应用程序“透明“。

 

分布式数据库中间:

       是面向应用程序提供的访问数据的中间件,使应用程序可以向访问普通数据库一样的操作分布式数据库。由分布式数据库中间件实现数据的分片路由。

       比如下面很常见的 MyCat 中间件:



分布式数据库中间件基础架构:



说明:

1.     SQL 解析模块:

一条 SQL 提交进来后,首先要解析出待查询的表以及表的分片字段,拿到表和分片字段后发给 SQL 路由模块进行计算。

 

2.     SQL 路由模块

根据获取到 SQL 解析模块传入的表和分片字段后,进行路由选择计算,以得到要真正访问的数据库服务器是哪个。

 

3.     SQL 执行代理模块:

根据 SQL 路由模块计算结果,将 SQL 发送给对应的数据库服务器进行执行。

(这里有可能根据 SQL 查询条件将 SQL 语句发送给多个数据库服务器进行执行)

 

4.     结果合并模块:

被发送给多个数据库服务器的 SQL 语句执行完毕后,数据库服务器都会将结果进行返回,所以需要结果合并模块进行最终返回结果的合并,最终返回给应用程序。

 

1.2.4     常见的分部式数据库中间件

Amoeba:

Amoeba 是作为一个真正的独立中间件提供服务,即应用去连接 Amoeba 操作 MySQL 集群,就像操作单个 MySQL 一样。从架构中可以看来,Amoeba 算中间件中的早期产品,后端还在使用 JDBC Driver。



Cobar:

Cobar 是在 Amoeba 基础上进化的版本,一个显著变化是把后端 JDBC Driver 改为原生的 MySQL 通信协议层。

后端去掉 JDBC Driver 后,意味着不再支持 JDBC 规范,不能支持 Oracle、

PostgreSQL 等数据。但使用原生通信协议代替 JDBC Driver,后端的功能增加了很多想象力,比如主备切换、读写分离、异步操作等。

 


MyCat:

       MyCat 又是在 Cobar 基础上发展的版本,两个显著点是:

1.     后端由 BIO 改为 NIO,并发量有大幅提高。

2.     增加了对 Order By、Group By、limit 等聚合功能的支持(虽然 Cobar 也可以支持 Order By、Group By、limit 语法,但是结果没有进行聚合,只是简单返回给前端,聚合功能还是需要业务系统自己完成)。

 


目前社区情况: 

1.     Amoeba 处于停滞状态

2.     Cobar 处于停滞状态

3.     MyCat 社区非常活跃

 

1.2.5     路由选择配置示例



说明:表示按 ID 作为分片字段,和 2 进行取余操作,结果为 0 则表示使用 blogdb-1 数据库;结果为 1 则表示使用 blogdb-2 数据库。

 

1.2.6     数据库服务器集群的伸缩



如果添加一台新的数据库服务器,使用余数 Hash 选择服务器时则需要考虑数据迁移的问题:

通常会在一个数据库服务器上部署多个数据库 Schema,当需要进行数据迁移时,将这些数据库服务器上的部分 Schema 迁移到新的服务器上即可,这样不用修改余数 Hash 的除数,比如下图:



一开始我们的采用余数 Hash 对 12 取模(上图中有 12 个 Schema 所以相当于分了 12 片,这个数量注意必须在开始的时候规划好!),此时 12 片分别部署在 3 台数据库服务器上;如果增加一台新的数据库服务器,则只需要将将现有 3 台服务器上的部分 Schema 迁移到新服务器上即可。

这样做对于数据库服务器集群的伸缩,唯一需要改变的就是路由选择配置文件中的对应不同余数时的对应数据库的配置了。

 

何时修改该路由选择配置文件?

只需要在新数据库数据迁移完毕后修改即可,迁移时可采用主从复制机制进行数据迁移,当新数据库服务器中的数据与原有数据库服务器数据一致后,就可以修改路由选择配置文件了(修改完毕后就可以删除原有数据库服务器上已经迁移的 Schema 了)。

1.3     数据库部署方案

1.3.1     单一服务与单一数据库

这是最简单的数据库部署方案,适用于数据库压力不大,可用性要求不高的简单场景。



1.3.2     主从复制实现伸缩

这是提供了读写分离的数据库部署方案,适用于数据库访问压力较大的情况。



1.3.3     两个 Web 服务及两个数据库

业务分库情况下的数据库部署方案,适用于应用进行业务分离的情况,对应的,不同业务应用访问的数据库服务器也需要进行分离了。



       注意:

              数据库在做分片之前,一般都需要先做业务分库哦

 

1.3.4     综合部署

一般当数据库访问压力增大时,建议先做业务分库;

如果业务分库后如果有些库中的表记录数太多,则可以进行数据分片;

同时可以使用主从复制来实现数据库的读写分离,降低服务器读压力,分摊服务器负载;

也可以使用主主复制来实现数据库的高可用。



2       CAP 原理

2.1     什么是 CAP 原理

CAP 原理指的是一个分布式系统最多只能同时满足一致性(Consistency)、可用性(Availability)和分区耐受性(Partition Tolerance)这三项中的两项:

C:Consistency:

一致性是说,每次读取的数据都应该是最近写入的数据或者返回一个错误(Every read

receives the most recent write or an error),而不是过期数据,也就是说,数据是一致

的。

 

A:Availability:

可用性是说,每次请求都应该得到一个响应,而不是返回一个错误或者失去响应,不过

这个响应不需要保证数据是最近写入的(Every request receives a (non-error)

response, without the guarantee that it contains the most recent write) ,也就是说系

统需要一直都是可以正常使用的,不会引起调用者的异常,但是并不保证响应的数据是

最新的。

 

P:Partiton Tolerance:

分区耐受性说,即使因为网络原因,部分服务器节点之间消息丢失或者延迟了,系统依

然应该是可以操作的。( The system continues to operate despite an arbitrary number of

messages being dropped (or delayed) by the network between nodes )

 


当网络分区失效发生的时候,我们要么取消操作,这样数据就是一致的,但是系统却不可用;要么我们继续写入数据,但是数据的一致性就得不到保证。

对于一个分布式系统而言,网络失效一定会发生,也就是说,分区耐受性是必须要保证的,那么在可用性和一致性上就必须二选一。

当网络分区失效,也就是网络不可用的时候,如果选择了一致性,系统就可能返回一个错误码或者干脆超时,即系统不可用。如果选择了可用性,那么系统总是可以返回一个

数据,但是并不能保证这个数据是最新的。

所以,关于 CAP 原理,更准确的说法是,在分布式系统必须要满足分区耐受性的前提下,可用性和一致性无法同时满足。

 

2.2     CAP 原理的注意事项

关于 CAP 原理,要特别注意的一点是,虽然说我们设计系统时不能同时保证拥有三点。但是也并不是说,保证了其中 2 点后,就要完全抛弃另外一点。只是相对的要做一些牺牲

比如在保证 CP 的情况下,虽然没办法保证高可用性,但这不意味着可用性为 0,我们可以通过合理的设计尽量的提高可用性,让可用性尽可能的接近 100%。

同理,在 AP 的情况下,也可以尽量的保证数据的一致性,或者实现弱一致性,即最终一致性。

实时证明,大多数都是牺牲了一致性。

像 12306 还有淘宝网,就好比是你买火车票,本来你看到的是还有一张票,其实在这个时刻已经被买走了,你填好了信息准备买的时候发现系统提示你没票了。这就是牺牲了一致性。

但是不是说牺牲一致性一定是最好的。就好比 mysql 中的事务机制,张三给李四转了 100 块钱,这时候必须保证张三的账户上少了 100,李四的账户多了 100。因此需要数据的一致性,而且什么时候转钱都可以,也需要可用性。但是可以转钱失败是可以允许的。

所以最终如何取舍,要结合具体的业务来权衡。

 

2.3     CAP 原理与数据存储冲突



客户端 1 和客户端都更新了 id=55 的商品价格,但他们连接的数据节点不同,此时由于节点之间通信失败,导致客户端 2 和客户端 4 查询的价格不同。

这就是满足可用性的情况下,但是牺牲了数据一致性。

 

3       数据最终一致性

3.1   最终一致性概念

存储系统可以保证:对于同一个数据对象,如果没有更多的更新产生,最终所有的访问都会返回最新更新的数据(版本)。



如上图所示,虽然在一段时间内客户端 A 更新数据后,客户端 C 并没有立即拿到最新的值,但经过一段时间后,服务器 2 也将正确数据同步过来了,所以最终客户端 C 也能拿到最新的更新后的数据了。

 

3.2   最终一致性写冲突处理策略

简单冲突处理策略:根据时间戳,最后写入覆盖



如上图,客户端 1 和客户端 2 都修改了同一个商品的价格,则为了保证数据最终一致性,最终写入时按照时间戳进行最后的写入覆盖。

 

3.3   客户端冲突解决

客户端可根据具体业务进行不通客户端数据的合并。



3.4   投票解决冲突( Cassandra )

当客户端 A 写入数据时,尝试写入 3 份数据,这 3 份数据至少有 2 份数据写入成功时,就会返回操作成功的响应。

当客户端 B 去读取数据时,尝试读取 3 份数据,这 3 份数据至少 2 份数据读取成功并一致时,才算做读取成功;如果返回的 2 份数据不一致,则还是要等第 3 份数据恢复后再次读取,然后比较所读取到的总共这 3 份数据的一致性,最终通过投票确定返回的数据。




4       HBase 架构

HBase 是一个分布式的、面向列的开源数据库,它在 Hadoop 之上提供了类似于 Bigtable 的能力。HBase 是 Apache 的 Hadoop 项目的子项目。HBase 不同于一般的关系数据库,它是一个适合于非结构化数据存储的数据库。

       用 HBase 技术可在廉价 PC Server 上搭建起大规模结构存储集群。

 


HBase 利用 Hadoop HDFS 作为其文件存储系统

HBase 通过 Hadoop HDFS 的数据复制实现数据存储的高可用。

 

-Hadoop HDFS 为 HBase 提供了高可靠性的底层存储支持;

 

-Hadoop MapReduce 为 HBase 提供了高性能的计算能力;

 

-Zookeeper 为 HBase 提供了稳定服务和 failover 机制(HMaster 往往是部署在多台服务器上的,而 Zookeeper 解决 HMaster 高可用的问题;通过 Zookeeper 进行主 HMaster 的选举,防止出现 HMaster“脑裂“的情况);

 

-Pig 和 Hive 为 HBase 提供了高层语言支持,使得在 HBase 上进行数据统计处理变的非常简单。

 

-Sqoop 则为 HBase 提供了方便的 RDBMS 数据导入功能,使得传统数据库数据向 HBase 中迁移变的非常方便。

 

-HMasterHBase 中可以启动多个 HMaster,通过 Zookeeper 的 Master Election 机制保证总有一个 Master 运行,HMaster 在功能上主要负责 Table 和 Region 的管理工作:

1. 管理用户对 Table 的增、删、改、查操作

2. 管理 HRegionServer 的负载均衡,调整 Region 分布

3. 在 Region Split 后,负责新 Region 的分配

4. 在 HRegionServer 停机后,负责失效 HRegionServer 上的 Regions 迁移

 

-HRegionServerHRegionServer 主要负责响应用户 I/O 请求,向 HDFS 文件系统中读写数据,是 HBase 中最核心的模块。

HRegionServer 内部管理了一系列 HRegion 对象,每个 HRegion 对应了 Table 中的一个 Region,HRegion 中由多个 HStore 组成。每个 HStore 对应了 Table 中的一个 Column Family 的存储,可以看出每个 Column Family 其实就是一个集中的存储单元,因此最好将具备共同 IO 特性的 column 放在一个 Column Family 中,这样最高效。

      

HBase 访问过程如下所示:



HBase 存储结构(Log Structed Merge Tree):         

为了让 HBase 支持已写入数据的修改或删除,HBase 的底层存储结构使用了

LSM 树结构:



5       ACID 与 BASE

ACID:指传统关系型数据库的事务机制,它具有以下特性:

原子性( Atomicity ):事务要么全部完成,要么全部取消。如果事务崩溃 ,状态回到

事务之前(事务回滚)。

隔离性(Isolation): 如果 2 个事务 T1 和 T2 同时运行,事务 T1 和 T2 最终的结果是

相同的,不管 T1 和 T2 谁先结束 A 隔离性主要依靠锁实现。

持久性( Durability ):一旦事务提交,不管发生什么(崩溃或者出错), 数据要保存

在数据库中。

一致性 ( Consistency ):只有合法的数据(依照关系约束和函数约束)才能写入数

据库。

 

BASE: BASE 是 Basically Available(基本可用) 、Soft-state(软状态) 和 Eventually Consistent(最终一致性) 三个短语的缩写:

基本可用( Basically Available ):系统在出现不可预知故障时,允许损失部分可用性,

如响应时间上的损失或功能上的损失。

Soft state (弱状态):软状态,指允许系统中的数据存在中间状态,并认为该中间状态

的存在不会影响系统的整体可用性,即允许系统在不同节点的数据副本之间进行数据同

步的过程存在延时。

Eventually consistent (最终一致性):指系统中所有的数据副本,在经过段时间的同

步后,最终能够达到一个致的状态,因此最终一致性的本质是需要系统保证数据能够

达到一致,而不需要实时保证系统数据的强一致性。

 

6       分布一致性 ZooKeeper

ZooKeeper 是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。

它是一个分布式的,开放源码的分布式应用程序协调服务,是 Google 的 Chubby 一个开源的实现,是 Hadoop 和 Hbase 的重要组件。

ZooKeeper 的目标就是封装好复杂易出错的关键服务,将简单易用的接口和性能高效、功能稳定的系统提供给用户。

ZooKeeper 是以 Fast Paxos 算法为基础的,Paxos 算法存在活锁的问题,即当有多个 proposer 交错提交时,有可能互相排斥导致没有一个 proposer 能提交成功,而 Fast Paxos 作了一些优化,通过选举产生一个 leader (领导者),只有 leader 才能提交 proposer,具体算法可见 Fast Paxos。因此,要想弄懂 ZooKeeper 首先得对 Fast Paxos 有所了解。 

 

ZooKeeper 的基本运转流程:

1、选举 Leader。

2、同步数据。

3、选举 Leader 过程中算法有很多,但要达到的选举标准是一致的。

4、Leader 要具有最高的执行 ID,类似 root 权限。

5、集群中大多数的机器得到响应并接受选出的 Leader。 

 

6.1     分布式系统脑裂

在一个分布式系统中,不同服务器获得了互相冲突的数据信息或者执行指令,导致整个

集群陷入混乱,数据损坏,称作分布式系统脑裂。

       比如当两台高可用服务器在指定的时间内,无法互相检测到对方心跳而各自启动故障转移功能,取得了资源以及服务的所有权,而此时的两台高可用服务器对都还活着并作正常运行,这样就会导致同一个服务在两端同时启动而发生冲突的严重问题,最严重的就是两台主机同时占用一个 VIP 的地址(类似双端导入概念),当用户写入数据的时候可能会分别写入到两端,这样可能会导致服务器两端的数据不一致或造成数据的丢失。

       下面是一个例子:



UserA 和 UserB 分别将自己的信息注册在 RouterA 和 RouterB 中。RouterA 和 RouterB 使用数据同步(2PC),来同步信息。那么当 UserA 想要向 UserB 发送一个消息的时候,需要现在 RouterA 中查询出 UserA 到 UserB 的消息路由路径,然后再交付给相应的路径进行路由。

当脑裂发生的时候,相当 RouterA 和 RouterB 直接的联系丢失了,RouterA 认为整个系统中只有它一个 Router,RouterB 也是这样认为的。那么相当于 RouterA 中没有 UserB 的信息,RouterB 中没有 UserA 的信息了,此时 UserA 再发送消息给 UserB 的时候,RouterA 会认为 UserB 已经离线了,然后将该信息进行离线持久化。

 

下面是一个数据库主主复制的例子:



应用程序正常运行时,只能有一个主节点对应用程序提供服务;

如果正在服务的主节点 A 失效了(比如宕机),则需要切换到主节点 B 进行服务;

此时需要通过建立一个管理中心节点(协调者),来确定当前哪台服务器做为提供服务的主节点;

协调者也可能失效,所以也需要对协调者做备份,也要做集群。这时候,问题来了,这么多协调者,到底听谁的呢?

Paxos 算法就是为了解决这个问题而生的

 

6.2     分布式一致性算法 Paxos

Paxos 算法是莱斯利·兰伯特(Leslie Lamport)1990 年提出的一种基于消息传递的一致性算法。

Paxos 算法解决的问题是一个分布式系统如何就某个值(决议)达成一致。

Paxos 算法运行在允许宕机故障的异步系统中,不要求可靠的消息传递,可容忍消息丢失、延迟、乱序以及重复。

它利用大多数 (Majority) 机制保证了 2F+1 的容错能力,即 2F+1 个节点的系统最多允许 F 个节点同时出现故障。

 

Paxos 将系统中的角色分为提议者 (Proposer),决策者 (Acceptor),和最终决策学习者 (Learner):



·         Proposer: 提出提案 (Proposal)。Proposal 信息包括提案编号 (Proposal ID) 和提议的值 (Value)。

·         Acceptor:参与决策,回应 Proposers 的提案。收到 Proposal 后可以接受提案,若 Proposal 获得多数 Acceptors 的接受,则称该 Proposal 被批准。

·         Learner:不参与决策,从 Proposers/Acceptors 学习最新达成一致的提案(Value)。

 

Paxos 算法通过一个决议分为两个阶段(Learn 阶段之前决议已经形成):

1.     第一阶段:Prepare 阶段。Proposer 向 Acceptors 发出 Prepare 请求,Acceptors 针对收到的 Prepare 请求进行 Promise 承诺。

2.     第二阶段:Accept 阶段。Proposer 收到多数 Acceptors 承诺的 Promise 后,向 Acceptors 发出 Propose 请求,Acceptors 针对收到的 Propose 请求进行 Accept 处理。

3.     第三阶段:Learn 阶段。Proposer 在收到多数 Acceptors 的 Accept 之后,标志着本次 Accept 成功,决议形成,将形成的决议发送给所有 Learners。



Paxos 算法流程中的每条消息描述如下:

·         Prepare: Proposer 生成全局唯一且递增的 Proposal ID (可使用时间戳加 Server ID),向所有 Acceptors 发送 Prepare 请求,这里无需携带提案内容,只携带 Proposal ID 即可。

·         Promise: Acceptors 收到 Prepare 请求后,做出“两个承诺,一个应答”。

两个承诺:

1. 不再接受 Proposal ID 小于等于(注意:这里是<= )当前请求的 Prepare 请求。

2. 不再接受 Proposal ID 小于(注意:这里是< )当前请求的 Propose 请求。

一个应答:

不违背以前作出的承诺下,回复已经 Accept 过的提案中 Proposal ID 最大的那个提案的 Value 和 Proposal ID,没有则返回空值。

·         Propose: Proposer 收到多数 Acceptors 的 Promise 应答后,从应答中选择 Proposal ID 最大的提案的 Value,作为本次要发起的提案。如果所有应答的提案 Value 均为空值,则可以自己随意决定提案 Value。然后携带当前 Proposal ID,向所有 Acceptors 发送 Propose 请求。

·         Accept: Acceptor 收到 Propose 请求后,在不违背自己之前作出的承诺下,接受并持久化当前 Proposal ID 和提案 Value。

·         Learn: Proposer 收到多数 Acceptors 的 Accept 后,决议形成,将形成的决议发送给所有 Learners。

 

6.3     Zab 协议

Zab 协议的全称是 Zookeeper Atomic Broadcast (Zookeeper 原子广播)Zookeeper 是通过 Zab 协议来保证分布式事务的最终一致性。

Zab 协议是为分布式协调服务 Zookeeper 专门设计的一种支持崩溃恢复的原子广播协议 ,是 Zookeeper 保证数据一致性的核心算法。Zab 借鉴了 Paxos 算法,但又不像 Paxos 那样,是一种通用的分布式一致性算法。它是特别为 Zookeeper 设计的支持崩溃恢复的原子广播协议。

在 Zookeeper 中主要依赖 Zab 协议来实现数据一致性,基于该协议,zk 实现了一种主备模型(即 Leader 和 Follower 模型)的系统架构来保证集群中各个副本之间数据的一致性。

这里的主备系统架构模型,就是指只有一台客户端(Leader)负责处理外部的写事务请求,然后 Leader 客户端将数据同步到其他 Follower 节点。

Zookeeper 客户端会随机的链接到 zookeeper 集群中的一个节点,如果是读请求,就直接从当前节点中读取数据;如果是写请求,那么节点就会向 Leader 提交事务,Leader 接收到事务提交,会广播该事务,只要超过半数节点写入成功,该事务就会被提交。

Zab 协议的特性:

1)Zab 协议需要确保那些已经在 Leader 服务器上提交(Commit)的事务最终被所有的服务器提交。 2)Zab 协议需要确保丢弃那些只在 Leader 上被提出而没有被提交的事务。

 

Zab 协议原理:

Zab 协议要求每个 Leader 都要经历三个阶段:发现,同步,广播。

·         发现:要求 zookeeper 集群必须选举出一个 Leader 进程,同时 Leader 会维护一个 Follower 可用客户端列表。将来客户端可以和这些 Follower 节点进行通信。

·         同步:Leader 要负责将本身的数据与 Follower 完成同步,做到多副本存储。这样也是提现了 CAP 中的高可用和分区容错。Follower 将队列中未处理完的请求消费完成后,写入本地事务日志中。

·         广播:Leader 可以接受客户端新的事务 Proposal 请求,将新的 Proposal 请求广播给所有的 Follower。

 

Zab 协议的核心:定义了事务请求的处理方式

1)所有的事务请求必须由一个全局唯一的服务器来协调处理,这样的服务器被叫做 Leader 服务器。其他剩余的服务器则是 Follower 服务器。

2)Leader 服务器 负责将一个客户端事务请求,转换成一个 事务 Proposal,并将该 Proposal 分发给集群中所有的 Follower 服务器,也就是向所有 Follower 节点发送数据广播请求(或数据复制)

3)分发之后 Leader 服务器需要等待所有 Follower 服务器的反馈(Ack 请求),在 Zab 协议中,只要超过半数的 Follower 服务器进行了正确的反馈后(也就是收到半数以上的 Follower 的 Ack 请求),那么 Leader 就会再次向所有的 Follower 服务器发送 Commit 消息,要求其将上一个事务 proposal 进行提交。

 



关于 Zab 协议更为详细的描述可参看:https://www.jianshu.com/p/2bceacd60b8a

 

6.4     ZooKeeper 基本架构



1 每个 Server 在内存中存储了一份数据;2 Zookeeper 启动时,将从实例中选举一个 leader(Paxos 协议);3 Leader 负责处理数据更新等操作(Zab 协议);4 一个更新操作成功,当且仅当大多数 Server 在内存中成功修改数据。



6.5     ZooKeeper Server 节点的数目

Zookeeper Server 数目一般为奇数 

Leader 选举算法采用了 Paxos 协议;Paxos 核心思想:当多数 Server 写成功,则任务数据写 

成功。也就是说: 

如果有 3 个 Server,则两个写成功即可; 

如果有 4 或 5 个 Server,则三个写成功即可。 

Server 数目一般为奇数(3、5、7) 

如果有 3 个 Server,则最多允许 1 个 Server 挂掉; 

如果有 4 个 Server,则同样最多允许 1 个 Server 挂掉 

既然如此,为啥要用 4 个 Server?根本不需要喽。

 

6.6     ZooKeeper 数据模型



组织结构

  zookeeper 采用层次化的目录结构,命名符合常规文件系统规范;   每个目录在 zookeeper 中叫做 znode,并且其有一个唯一的路径标识; Znode  Znode 可以包含数据和子 znode(ephemeral 类型的节点不能有子 znode);   Znode 中的数据可以有多个版本,比如某一个 znode 下存有多个数据版本,那么查询这个路径下的数据需带上版本;   客户端应用可以在 znode 上设置监视器(Watcher)   znode 不支持部分读写,而是一次性完整读写 Znode 类型  Znode 有两种类型,短暂的(ephemeral)和持久的(persistent);   Znode 的类型在创建时确定并且之后不能再修改;   ephemeral znode 的客户端会话结束时,zookeeper 会将该 ephemeral znode 删除,ephemeralzn ode 不可以有子节点;   persistent znode 不依赖于客户端会话,只有当客户端明确要删除该 persistent znode 时才会被删除;   Znode 有四种形式的目录节点,PERSISTENT、PERSISTENT_SEQUENTIAL、EPHEMERAL、PHEMERAL_SEQUENTIAL。

 

6.7     ZooKeeper 性能

Zookeeper 是以高吞吐量为目标进行设计的,故而在读多写少的场合有非常好的性能表现。如下图所示:



纵轴为每秒响应的客户端请求数,横轴为读请求所占百分比。

从图中可以清晰的看到,随着读请求所占百分比的提高,Zookeeper 的 QPS 也不断提高。

 

6.8     ZooKeeper 典型应用场景

1、数据发布和/订阅

  主要的一个场景,比如配置中心。我们会将配置的相关信息都存放在一个中心,这样我们的应用就不用每次修改参数就要进行重启,使用了 zk 作用配置中心的数据推送更新,这样我们就能方便的进行数据更新,每次将相关数据发布到配置中心,然后由应用服务去订阅,这样就能动态的进行配置数据的更新。

2、负载均衡

  可以基于 ZK 来实现 DDNS 动态域名解析服务,从而达到域名的动态添加、修改、删除等。能够基于域名服务,进行应用的负载,从而达到请求负载到各个应用中。

3、命名服务

  命名服务,主要的应用场景在于 rpc 服务,比如 dubbo 等框架,可以将相应的服务注册在 zk 上,这样服务调用就可以根据其所命名的服务来提供对外服务等。

4、分布式协调/通知

  对于一个在多台机器部署运行的应用上,通常都需要一个协调者来控制整个系统的运行流程。比如分布式事务、机器间的互相协调等。这样能将分布式协调的职责能从应用中分离出来,达到减少系统间的耦合性,提高系统的可扩展性。

5、集群管理

  在集群环境中,机器和应用都是分散着进行部署,每次进行服务的上下线升级的过程中,都要手动进行集群的管理,这样造成人做的事比较重复性,并且也比较麻烦容易出错。如果能使用 zk 来协助我们进行服务或机器进群的管理,这样将能帮助我们解决需要繁琐又麻烦的事。

6、Master 选举

  Master 选举,也就是在众多机器或服务中,选举出一个最终“决定权”的领导者,来独立完成一项任务。比如有一项服务是需要对外提供服务,但是要保证高可用,我们就机会进行服务的多项部署,也就是做了一些备份,提高系统的可用性。一旦我们的主服务挂了,我们可以让其它的备份服务进行重新选举,这样我们就能使整个系统不会因服务的挂掉而造成服务不可用。

7、分布式锁

  分布式锁是控制分布式系统间同步访问共享资源的一种方式。如果不同的系统或同一个系统的不同主机之间共享了同一个资源,那么访问这些资源的时候,需要使用互斥的手段来防止彼此之间的干扰,以保证一致性,这种情况就需要使用分布式锁。

8、分布式队列

  使用 zk 来实现分布式队列,分为两大类:FIFO 先进先出队列、Barrier 分布式屏障。FIFO 队列是一种很典型的队列模型:先进入队列的请求先完成操作后,才会处理后面的请求;Barrier 分布式屏障,则是需要将队列元素都集聚之后才进行统一的执行安排,否则只能等待。

 

7       案例:Doris-海量 KV Engine 的设计

Doris 是一种支持 Key、Value 数据结构的分布式存储系统,核心要解决的问题是分布式路由、分布式集群伸缩、分布式数据冗余与失效转移,它是阿里巴巴的一个内部产品。

 

Doris 的主要访问模型是,应用程序 KV Client 启动后,连接控制中心 Administration,从控制中心获得整个 Doris 集群的服务器部署信息及路由算法,Client 使用 Key 作为参数进行路由计算,计算得到集群中某些服务器作为当前 Key、Value 数据存储的服务器节点;然后 KV Client 使用自定义的通信协议将数据和命令传输给服务器上的 Data Server 组件,DataServer 再调用本地的 Berkeley DB 将数据存储到本地磁盘。

 

Doris 的核心技术就是这个架构模型上创新性地实现了自己独特的分区路由算法、失效转移策略、集群伸缩设计方案。并在项目开发过程中,将这个三个技术创新申请了技术专利。

 

7.1     Doris 基本访问架构

Doris 将存储服务器集群分成多个 group(默认情况下为 2 个 group),数据写操作的时候,根据分区路由算法,在每个 group 里计算一个服务器地址,异步并发同时向多个 group 的服务器上写入数据,以此保证数据有多个备份。

 


7.2     分区路由算法



Doris 采用一种基于虚拟节点的分区路由算法,Key 使用余数 Hash 算法计算得到虚拟节点下标。(虚拟节点下标 = hash(md5(key)) mod 虚拟节点个数

 

虚拟节点和物理服务器节点之间计算建立一个映射关系,通过映射关系查找实际要访问的物理服务器 IP 地址。

 

路由算法在初始化的时候就预先设立一个较大的数字,比如 100000,当存储服务器集群需要伸缩的时候,要增加一个服务器,虚拟节点和下标计算算法不变,仅仅调整虚拟节点和物理服务器节点的映射关系就可以了

 

这种基于虚拟节点的分区路由算法相对于传统的一致性 Hash 路由算法,可以获得更好的数据负载均衡,即数据在各个服务器上的存储分布更加均衡。在集群伸缩、增加服务器的时候可以做到更少迁移数据。

在实践中,这种算法的一个更大优势是,如果将物理存储的文件系统和虚拟节点关联,即一个虚拟节点对应一个物理存储文件,那么当集群扩容,进行数据迁移的时候,就可以以文件为单位进行数据拷贝,这样迁移速度和运维成本都非常低。

 

这里有一个问题需要注意,在增加服务器的时候,如何重新计算虚拟节点与物理节点的映射关系,使新的映射关系依然处于负载均衡的状态

其实可以采用节点轮询的方式实现,即添加新的服务器后,按余数法重新计算下每个服务器应该对应多少虚拟节点,假设计算结果为 n,则轮询各个原有节点上的虚拟节点个数,个数超过 n 的虚拟节点都应该迁移到新增加的服务器上去。

 

7.3     失效转移策略

当 KV Client 访问某台服务器失败的时候,Doris 会启动失效转移策略。具体来说,Doris 将失效分为三种情况:瞬时失效、临时失效、永久失效

 

不同情况采用不同的失效转移策略,具体过程如下:

 


当第一次不能访问服务器的时候,Doris 认为这是瞬时失效,会进行访问重试;

如果三次重试后仍然失败,就会把失败信息提交给控制中心。

控制中心检测该服务器心跳是否正常,并进行尝试访问,如果访问失败,就将该服务器标记为临时失效,并通知所有 KV Client 应用程序。



KV Client 应用程序收到服务器失效通知的时候,启动临时失效策略,将原本需要写入到失效节点(图中的物理节点 2)的数据写入临时日志节点(图中的物理节点 X),而读操作则只访问正常的物理节点 1。

当临时失效节点 2 恢复正常运行,系统会将失效期间写入临时日志节点 X 的数据合并恢复到物理节点 2,这段时间物理节点 2 只提供写服务,不提供读服务。当所有数据恢复完毕,集群访问恢复正常。

 

而对于永久失效的节点,需要添加新的服务器以代替下线的服务器,基本策略就是将另一个 group 正常使用的服务器数据拷贝到新添加的服务器上即可。



需要说明的是,上述三种失效转移过程,除了服务器永久失效后,需要工程师手动添加服务器,并到控制中心添加新服务器配置、激活启用外,其他情况不需要任何人工干预,全部自动化完成。

7.4     集群伸缩设计

分布式系统的一个重要设计目标是集群弹性可伸缩,如果当前的服务器数目不能满足业务的负载压力要求,那么就添加更多的服务器去增强处理能力。

对于分布式数据存储服务器的伸缩性扩容而言,必然伴随着数据的迁移,就是将原先服务器中的部分数据迁移到新的服务器上。



具体过程为:

1. 向集群中一个分组 group 添加新的物理服务器,部署并启动 Doris 服务器进程。

2. 将这个 group 的所有服务器设置为临时失效。

3. 使用路由算法重新计算加入服务器后的虚拟节点分布,并把需要迁移的虚拟节点对应的物理文件拷贝到新服务器上。

4. 设置 group 所有服务器临时失效恢复,将扩容期间的数据更新写回到这些服务器。

 

用户头像

卧石漾溪

关注

还未添加个人签名 2020.05.04 加入

还未添加个人简介

评论

发布
暂无评论
Week 06 学习总结