Redis集群

  2019-9-23 


集群,虽然没用过,但了解一下…

主从同步

  1. 主从同步是分布式的基础

  2. 分布式基础理论:CAP原理

    C - Consistent ,一致性

    A - Availability ,可用性

    P - Partition tolerance ,分区容忍性

    网络分区:分布式系统的结点断开网络

    一句话概括 CAP 原理就是——网络分区发生P时,一致性C和可用性A两难全

    Redis保证最终一致性

  3. Redis主从同步:

    增量复制(增量同步):增量追赶,写指令流同步(从库执行指令流) 维持一个指令buffer(是个环形数组)

    全量复制(快照同步):增量太慢了,指令buffer塞满旧指令还没同步就被覆盖,所以要全量追赶,即快照同步;指令bgsave

    无盘复制(快照同步):进行快照同步的时候,跨越对磁盘的IO操作(快照化,负荷很高),而是直接通过socket传给从结点,然后结点将接收到的内容存入磁盘再加载快照

    前面三个都是异步复制,下面的wait是同步复制

    wait指令:主从同步wait之前的指令,是阻塞的,这时候不可用

    生产快照是一个遍历的过程 主从主要是为了保障Redis的高可用性,同时也能兼顾提升性能

    增量同步的buffer要合适,否则一直赶不上指令增加的速度,那就得经常全量复制,影响效率

如果redis只是做缓存,则无需做主从同步

集群方案:Sentinel哨兵——自动主从切换

可以将Redis Sentinel 集群看成是一个 ZooKeeper 集群

sentinel的作用:

  1. 监控主从节点的健康,当主节点挂掉时,自动选择一个最优的从节点切换为主节点。

  2. 提供集群内节点的IP地址给客户端访问(客户端该访问集群中哪台服务器由sentinel决定)

    客户端来连接集群时,会首先连接 sentinel,通过 sentinel 来查询主节点的地址,然后再去连接主节点进行数据交互。当主节点发生故障时,客户端会重新向 sentinel 要地址,sentinel 会将最新的主节点地址告诉客户端。

  3. 可以控制何时停止对外服务(可用性丧失)

    比如可以规定当还有多少从结点可用的时候才对外提供服务,以及延迟为多少判断从节点可用

集群方案:Codis

Codis是中心化的,Codis作为代理。

为什么要聚集多个Redis实例?因为单个Redis实例都是单核心单线程的。

Codis将众多小内存的 Redis 实例综合起来,将分布在多台机器上的众多 CPU 核心的计算能力聚集到一起,完成海量数据存储和高并发读写操作。

Codis 上挂接的所有 Redis 实例构成一个 Redis 集群,当集群空间不足时,可以通过动态增加 Redis 实例来实现扩容需求。同一片Redis集群也可以有多个Codis(容灾、增加QPS)

在Codis集群中,所有的redis表(key-value)相当于连在了一起成为了一张大表,一个redis储存部分的key-value,那么Codis 要负责将特定的 key 转发到特定的 Redis 实例Codis通过hash计算再取模得到一个余数来确定该key对应储存到哪个槽位,而一个槽位映射到一个redis实例

(举例:假如有4个redis,那么1024个槽位,每个redis就映射到256个槽位)

Codis 还需要一个分布式配置存储数据库(zooKeeper/etcd)专门用来持久化槽位关系,以供多个Codis共享一个槽位映射

于是Codis管理的redis集群扩容的时候,槽位就要被重新划分,一部分槽位对应的key会迁移到另一个/些redis集群中。Codis提供槽位-redis实例映射自动均衡(由于key需要迁移,所以单个 key 对应的 value 不宜过大)

事务只能在单个redis中完成,由于一个事务的多个key可能储存在多个redis中,所以codis不支持事务只要涉及多个key操作的指令都不支持

Codis由于将分布式配置问题交给第三方zooKeeper/etcd去解决,所以比redis亲儿子集群方案redis clusters简单很多

集群方案:Redis Cluster

Redis Clusters是去中心化集群,没有Codis或Sentinel那样的代理作为客户访问的中心

它也和Codis一样,相当于将所有的Redis key-value形成一张大表,将所有数据划分为 16384 个 槽,它比 Codis 的 1024 个槽划分的更为精细每个节点负责其中一部分槽位

然而由于没有代理,客户端每一次访问集群(访问集群中任意一个结点)都会获得槽位配置信息表,由要查找某key的客户端自己去定位到目标结点

那么我们就要用纠正机制来确保客户端与服务器的槽位信息一致,且每个结点会将槽位配置信息持久化

redis cluster提供了迁移工具redis-trib来手动调整槽位分配(codis可以自动调整以让集群负载均衡,且有UI界面),需要手动调整;迁移过程是阻塞的;结点迁移经过了从原结点复制数据,储存到目标结点,删除原结点数据的过程

当①槽位发生过迁移(已经完成迁移),客户端联系目标结点,此时目标槽位已经不归它管,它就会返回一个moved指令和目标结点地址,客户端就去找新的目标结点;但是②在迁移过程中,原结点无法判断新结点是否已经完成迁移(这时候原结点有还没来得及删那就直接返回,没有就要给目标新结点地址),未完成迁移那么新结点就暂时还不认识该槽位,客户端就需要去新结点执行asking指令,告诉新结点,那么新结点就会临时纠正槽位movedasking都有最大重试次数,避免死循环

集群发生结点变更(比如目标结点挂掉)的时候,客户端会连接异常/错误并重连,这就相当于客户端得到了通知

redis cluster提供主从切换,自动切换主从结点,且提供了一种防止网络抖动的机制,即当timeout超过一定时间才进行主从切换

如果某个结点A下线了,被集群中的其中一个结点B发现,那么它就会像整个集群广播,那么整个集群都会知道结点B认为结点A下线了,当集群中大部分结点都认为A下线了,那么整个集群所有结点就认定该结点A下线了

不支持事务多机请求无法实现原子性,理由同codis)

key和服务器映射关系算法

在redis集群(作为数据缓存)中使用【hash对服务器数量取模算法】定位服务器,若【服务器数量发生改变(或部分结点失效)】,所有【缓存在一定时间内失效】(比如4台服务器,那么计算目标结点序号就是hash(data)%4=2,那么就去2机器上找,当服务器数量变了,hash公式计算出来的目标服务器就变了,但是数据还是没有变动),当应用无法从缓存中获取数据的时候(认为缓存中没有),它就会去向后端数据库请求,造成数据库压力过大崩溃,此即【缓存雪崩】。所以我们不能用最朴素的hash方法。

hash slot

redis cluster和codis都是采用hash slot方法来做key和服务器的映射关系(而不是一致性hash),是固定分配数据与服务器对应的槽位的(所以服务器变更也要迁移数据),所以也不会有缓存雪崩问题

一致性hash

我们让数据和服务器都对2^32取模,将数据和服务器映射到同一个hash环中,每个数据储存在于沿逆时针走的第一个服务器结点中。这样一来,一旦有结点挂了,那么只会影响hash环中的部分段数据,而不会影响整个集群!我们只需要重定位部分数据即可。

(服务器映射到hash环可以采用将服务器的IP或主机名来进行hash)

当遇到数据倾斜,即服务器在环上分布不均匀使得某台服务器分配过多数据,这样就起不到我们的挂服务器只影响部分数据的效果了,那我们就可以增加一些虚拟服务器结点,虚拟服务器结点映射到实际服务器结点即可。数据定位的时候也是先映射到虚拟结点再映射到实际结点。


且听风吟