分布式
为什么需要分布式系统?
单台机器的资源有极限
解决单点故障:在单台机器上部署服务,如果机器出现故障导致服务挂了,那么整个系统就会崩溃了。因此就要提高系统的可用性,就需要分布式系统。
分布式数据库
多副本让数据库的可用性和持久性有了保障,但是仍然有以下问题需要解决:
数据规模大到一定程度后,单个物理节点存放不了那么大的数据量;
主承受的读写压力太大,单台主节点承受不了这样高的 IOPS(吞吐能力)。
怎么解决?分布式。就是把数据分片存储到多台设备上的分片服务器一起构成一个单副本的数据库。分片的方式常见的有两种:
哈希分片(Hash based sharding);
范围分片(Range based sharding)。
分布式理论基础
CAP理论
C:Consistency(强一致性):如果系统对一个写操作返回成功,那么之后的读请求都必须读到这个新数据;如果返回失败,那么所有的读操作都不能读到这个数据。对调用者而言,数据具有强一致性。
A:Availability(可用性):所有读写请求在一定时间内得到响应,可终止,不会一直等待。
P:Partition tolerance(分区容错性):在网络分区的情况下,仍能正常对外服务。
分布式系统的节点往往都是分布在不同的机器上隔离开的,这意味着必然会有网络断开的风险,断开就叫「网络分区」。
在网络分区发生时,两个分布式节点之间无法进行通信,我们对一个节点进行的修改操作将无法同步到另外一个节点,此时数据的「一致性」将无法满足,此时有2个选择:
牺牲「可用性」,不再提供修改数据的功能,直到网络状况完全恢复正常再继续对外提供服务,以此确保对外提供服务时的数据一致性。
牺牲「一致性」,继续对外正常提供服务,只确保数据的最终一致性
一句话概括 CAP 原理就是——网络分区发生时,一致性和可用性两难全。
一致性与可用性的决择
对关系数据库来说,插入一条数据之后立刻查询,是肯定可以读出来这条数据的,但是对于很多web应用来说,并不要求这么高的实时性,比方说在微博发一条消息之后,过几秒乃至十几秒之后,我的订阅者才看到这条动态是完全可以接受的。
很多web实时系统并不要求严格的数据库事务,对读一致性的要求很低, 有些场合对写一致性要求并不高。允许实现最终一致性。
一致性和可用性之间取一个平衡。多余大多数web应用,其实并不需要强一致性。因此牺牲C换取P,这是目前分布式数据库产品的方向。
BASE理论
BASE就是为了解决关系数据库强一致性而引起的可用性降低而提出的解决方案。
BASE其实是下面三个术语的缩写:
基本可用(Basically Available):允许可用性降低(可能延长响应时间,和服务降级)。
软状态(Soft state):允许系统中的数据存在中间状态,比如:支付中。
最终一致(Eventually consistent):允许节点之间的数据同步存在延迟,但在一定的期限后达到数据最终的一致性。
它的思想是通过让系统放松对某一时刻数据一致性的要求来换取系统整体伸缩性和性能上改观。为什么这么说呢,缘由就在于大型系统往往由于地域分布和极高性能的要求,不可能采用分布式事务来完成这些指标,要想获得这些指标,我们必须采用另外一种方式来完成,这里BASE就是解决这个问题的办法
Redis 主从同步
Redis 支持主从同步和从从同步,从从同步功能是为了减轻主库的同步负担。一般为了描述上的方便,只说主从同步即可。
注:salve节点主要是做高可用,不分担查询压力。 redis 定位就是支持高性能的查询,所以操作主节点就够了
增量同步
Redis 同步的是指令流,主节点会将那些对自己的状态产生修改性影响的指令记录在本地的内存 buffer 中,然后异步将 buffer 中的指令同步到从节点,从节点一边执行同步的指令流来达到和主节点一样的状态,一边向主节点反馈自己同步到哪里了 (偏移量)。
因为内存的 buffer 是有限的,所以 Redis 主库不能将所有的指令都记录在内存 buffer 中。Redis 的复制内存 buffer 是一个定长的环形数组,如果数组内容满了,就会从头开始覆盖前面的内容。
如果因为网络状况不好,从节点在短时间内无法和主节点进行同步,那么当网络状况恢复时,Redis 的主节点中那些没有同步的指令在 buffer 中有可能已经被后续的指令覆盖掉了,从节点将无法直接通过指令流来进行同步,这个时候就需要用到更加复杂的同步机制 —— 快照同步。
快照同步
快照同步是一个非常耗费资源的操作,它首先需要在主库上进行一次 bgsave 将当前内存的数据全部快照到磁盘文件中,然后再将快照文件的内容全部传送到从节点。从节点将快照文件接受完毕后,立即执行一次全量加载,加载之前先要将当前内存的数据清空。加载完毕后通知主节点继续进行增量同步。
在整个快照同步进行的过程中,主节点的复制 buffer 还在不停的往前移动,如果快照同步的时间过长或者复制 buffer 太小,都会导致同步期间的增量指令在复制 buffer 中被覆盖,这样就会导致快照同步完成后无法进行增量复制,然后会再次发起快照同步,如此极有可能会陷入快照同步的死循环。
所以务必配置一个合适的复制 buffer 大小参数,避免快照复制的死循环。
增加从节点
当从节点刚刚加入到集群时,它必须先要进行一次快照同步,同步完成后再继续进行增量同步。
无盘复制
主服务器直接通过套接字将快照内容发送到从节点,生成快照是一个遍历的过程,主节点会一边遍历内存,一边将序列化的内容发送到从节点,从节点还是跟之前一样,先将接收到的内容存储到磁盘文件中,再进行一次性加载。
Redis主从同步小结
主从复制的快照和增量复制就是单个节点持久化两种方式RDB、AOF的变形,结合起来使用就类似混合持久化,本质上差不多,只不过一个是从本地内存到本地磁盘,一个是从主节点内存到从节点内存,中间通过无盘复制来减少不必要的“绕路”。
一致性哈希(Consistent Hashing)
一致性哈希本质上是一种路由寻址算法。
为什么需要一致性哈希?
数据库的主从结构,写请求限制只能在主节点上处理,导致了集群的写操作性能约等于单机,那么随着业务发展,集群的性能可能就扛不住了,会造成系统过载和服务不可用,这时该怎么办呢?
这时就要通过分集群,来突破单集群的性能限制,即多个主从结构小集群共同组成一个大集群对外提供服务。
可以这样做:加个 Proxy 层,由 Proxy 层处理来自客户端的读写请求,接收到读写请求后,通过对 Key 做哈希找到对应的集群。
但它有个明显的缺点:当需要变更集群数时(比如从 2 个集群扩展为 3 个集群),这时大部分的数据都需要迁移,重新映射,数据的迁移成本是非常高的。那么如何解决哈希算法,数据迁移成本高的痛点呢?答案就是一致性哈希。
为什么一定需要数据迁移呢?如果不迁移,之前存的数据有的就找不到了,例子:假设有一个 key,hash(key) = 4, hash(key) % 2 = 0,即之前为 2 个集群时,这个 key 是存在 0 号服务器的,现在扩展到 3 个集群,hash(key) % 3 = 1,集群扩展后,读写数据的时候就会到 1 号服务器去操作了。
一致性哈希是如何解决扩展集群需要大量迁移数据这个问题的?
一致哈希算法也用了取模运算,但与哈希算法不同的是,哈希算法是对节点的数量进行取模运算,而一致哈希算法是对 2^32 进行取模运算,这样取模操作后的值就是固定的了。那范围这么大,数据具体存到哪台服务器上呢?可以理解为一致哈希算法将整个哈希值空间组织成了一个哈希环:
比如通过服务器节点主机名执行 hash 算法映射到哈希环上,如上图有 3 个节点。要存取的 key 也通过 hash 算法确定在环上的位置,然后顺时针“行走”,遇到的第一个服务器节点就是该 key 所在节点。
节点宕机会发生什么呢?
可以看到,key-01 和 key-02 不会受到影响,只有 key-03 的寻址受影响,由于 C 节点宕机,key-03 哈希寻址顺时针查找,被重定位到 A 服务器。
节点扩容会发生什么呢?
原理一样,key-01、key-02 不受影响,只有 key-03 的寻址被重定位到新节点 D。
总的来说,使用了一致哈希算法后,扩容或缩容的时候,都只需要重定位环空间中的一小部分数据。
但还有这样的问题:客户端访问请求集中在少数的节点上,出现了有些机器高负载,有些机器低负载的情况。
有什么办法能让数据访问分布的比较均匀呢?
答案是虚拟节点。
由如上寻址定位所属服务器的原理可以看出,当服务器节点在哈希环上占据的区间差距比较大的时候,那么有的服务器就会分到较多的 key,有的较少,这样就会导致对服务器节点的访问冷热不均。
解决方案就是对每一个服务器节点计算多个哈希值,在每个计算结果位置上,都放置一个虚拟节点,并将虚拟节点映射到实际节点。比如,可以在主机名的后面增加编号,分别计算 “Node-A-01”“Node-A-02”“Node-B-01”“Node-B-02”“Node-C-01”“Node-C-02”的哈希值,于是形成 6 个虚拟节点:
增加虚拟节点后,节点在哈希环上的分布就相对均匀了。这时,如果有访问请求寻址到“Node-A-01”这个虚拟节点,将被重定位到节点 A。这样就解决了冷热不均的问题。
提升系统的容灾能力
由以上原理可知,当服务器节点越多的时候,每一个服务器在哈希环上占据的区间越小,分到的 key 就越少。
由此可知,使用一致性哈希实现哈希寻址时,可以通过增加服务器节点数来降低节点宕机对整个集群的影响,提升系统的容灾能力。
分布式事务
分布式锁
分布式和集群的区别
分布式:不同的多台服务器上面部署不同的服务模块(工程),他们之间通过Rpc/Rmi之间通信和调用,对外提供服务和组内协作。
集群:不同的多台服务器上面部署相同的服务模块,通过分布式调度软件进行统一的调度,对外提供服务和连接。
Last updated
Was this helpful?