浅析Redis集群

redis replication -> 主从架构 -> 读写分离 -> 水平扩容支撑读高并发

redis replication

  • 基本原理
    在这里插入图片描述
  • 核心机制
  1. redis采用异步方式复制数据到slave节点,不过redis 2.8开始,slave node会周期性地确认自己每次复制的数据量
  2. 一个master node可以配置多个slave node
  3. slave node也可以连接其他的slave node
  4. slave node做复制的时候,是不会block master node正常工作的
  5. slave node在做复制的时候,也不会block对自己的查询操作,它会用旧的数据集来提供服务; 但是复制完成的时候,需要删除旧数据集,加载新数据集,这个时候就会暂停对外服务了
  6. slave node主要用来进行横向扩容,做读写分离,扩容的slave node可以提高读的吞吐量
  • 复制的完整流程
  1. slave node启动,仅仅保存master node的信息,包括master node的host和ip,通过读取redis.conf里面的slaveof配置。
  2. slave node内部有个定时任务,每秒检查是否有新的master node要连接和复制,如果发现,就跟master node建立socket网络连接。
  3. slave node发送ping命令给master node。
  4. 口令认证,如果master设置了requirepass,那么salve node必须发送master auth的口令过去进行认证。
  5. master node第一次执行全量复制,将所有数据发给slave node
  6. master node后续持续将写命令,异步复制给slave node。
    在这里插入图片描述
  • 数据同步相关的核心机制
  1. master和slave都会维护一个offset
  2. backlog,默认是1MB大小,在master node中。master node给slave node复制数据时,也会将数据在backlog中同步写一份。backlog主要是用来做全量复制时中断情况下使用。
  3. info server命令可以看到master的run id。run id的作用,如下。如果需要不更改run id重启redis,可以使用redis-cli debug reload命令。
    在这里插入图片描述
  4. psync runid offset,从节点使用psync从master node进行复制。master node会根据自身的情况返回响应信息,可能是FULLRESYNC runid offset触发全量复制,可能是CONTINUE触发增量复制。
  • 全量复制,rdb生成、rdb通过网络拷贝、slave旧数据的清理、slave aof rewrite
  1. master执行bgsave,在本地生成一份rdb快照文件
  2. master node将rdb快照文件发送给salve node,如果rdb复制时间超过60秒(repl-timeout),那么slave node就会认为复制失败,可以适当调节大这个参数
  3. 对于千兆网卡的机器,一般每秒传输100MB,6G文件,很可能超过60s
  4. master node在生成rdb时,会将所有新的写命令缓存在内存中,在salve node保存了rdb之后,再将新的写命令复制给salve node
  5. client-output-buffer-limit slave 256MB 64MB 60,如果在复制期间,内存缓冲区持续消耗超过64MB,或者一次性超过256MB,那么停止复制,复制失败
  6. slave node接收到rdb之后,清空自己的旧数据,然后重新加载rdb到自己的内存中,同时基于旧的数据版本对外提供服务
  7. slave node接收到rdb之后,清空自己的旧数据,然后重新加载rdb到自己的内存中,同时基于旧的数据版本对外提供服务
  8. 如果slave node开启了AOF,那么会立即执行BGREWRITEAOF,重写AOF
  9. 如果复制的数据量在4G~6G之间,那么很可能全量复制时间消耗到1分半到2分钟
  • 增量复制
  1. 如果全量复制过程中,master-slave网络连接断掉,那么salve重新连接master时,会触发增量复制
  2. master直接从自己的backlog中获取部分丢失的数据,发送给slave node,默认backlog就是1MB
  3. msater就是根据slave发送的psync中的offset来从backlog中获取数据的
  • heartbeat
  1. 主从节点互相都会发送heartbeat信息。master默认每隔10秒发送一次heartbeat,salve node每隔1秒发送一个heartbeat

master持久化对于主从架构的安全保障的意义

  • 如果采用了主从架构,那么建议必须开启master node的持久化!不建议用slave node作为master node的数据热备,因为那样的话,如果你关掉master的持久化,可能在master宕机重启的时候数据是空的,然后可能一经过复制,salve node数据也丢了
  • master的各种备份方案,需要做。万一,本地的所有文件丢失了,从备份中挑选一份rdb去恢复master,这样才能确保master启动的时候,是有数据的。即使采用了高可用机制,slave node可以自动接管master node,但是也可能sentinal还没有检测到master failure,master node就自动重启了,还是可能导致上面的所有slave node数据清空。

redis主从架构

  • 核心原理
  1. 当启动一个slave node的时候,它会发送一个PSYNC命令给master node。
  2. 如果这是slave node重新连接master node,那么master node仅仅会复制给slave部分缺少的数据; 否则如果是slave node第一次连接master node,那么会触发一次full resynchronization。
  3. 开始full resynchronization的时候,master会启动一个后台线程,开始生成一份RDB快照文件,同时还会将从客户端收到的所有写命令缓存在内存中。RDB文件生成完毕之后,master会将这个RDB发送给slave,slave会先写入本地磁盘,然后再从本地磁盘加载到内存中。然后master会将内存中缓存的写命令发送给slave,slave也会同步这些数据。
  4. slave node如果跟master node有网络故障,断开了连接,会自动重连。master如果发现有多个slave node都来重新连接,仅仅会启动一个rdb save操作,用一份数据服务所有slave node。
    在这里插入图片描述
  • 主从复制的断点续传
  1. 从redis 2.8开始,就支持主从复制的断点续传,如果主从复制过程中,网络连接断掉了,那么可以接着上次复制的地方,继续复制下去,而不是从头开始复制一份。
  2. master node会在内存中保存一个backlog,master和slave都会保存一个replica offset还有一个master id,offset就是保存在backlog中的。如果master和slave网络连接断掉了,slave会让master从上次的replica offset开始继续复制。但是,如果没有找到对应的offset,那么就会执行一次resynchronization。
  • 无磁盘化复制
    master在内存中直接创建rdb,然后发送给slave,不会在自己本地落地磁盘了,通过repl-diskless-sync参数设置。repl-diskless-sync-delay参数,表示等待一定时长再开始复制,因为要等更多slave重新连接过来。
  • 过期key处理
    slave不会过期key,只会等待master过期key。如果master过期了一个key,或者通过LRU淘汰了一个key,那么会模拟一条del命令发送给slave。

redis哨兵架构

  • 什么是redis的不可用?
    在这里插入图片描述
  • redis基于哨兵的高可用性
    在这里插入图片描述
  • 哨兵的主要功能
  1. 集群监控,负责监控redis master和slave进程是否正常工作
  2. 消息通知,如果某个redis实例有故障,那么哨兵负责发送消息作为报警通知给管理员
  3. 故障转移,如果master node挂掉了,会自动转移到slave node上
  4. 配置中心,如果故障转移发生了,通知client客户端新的master地址
  • 哨兵集群
  1. 故障转移时,判断一个master node是宕机了,需要大部分的哨兵都同意才行
  2. 即使部分哨兵节点挂掉了,哨兵集群还是能正常工作的
  3. 目前采用的是sentinal 2版本,sentinal 2相对于sentinal 1来说,重写了很多代码,主要是让故障转移的机制和算法变得更加健壮和简单
  • 哨兵的核心知识
  1. 哨兵至少需要3个实例,来保证自己的健壮性
  2. 哨兵 + redis主从的部署架构,是不会保证数据零丢失的,只能保证redis集群的高可用性
  3. 对于哨兵 + redis主从这种复杂的部署架构,尽量在测试环境和生产环境,都进行充足的测试和演练
  • 为什么redis哨兵集群只有2个节点无法正常工作?
  1. 如果哨兵集群仅仅部署了个2个哨兵实例,quorum=1
  2. 2个哨兵的majority就是2(2的majority=2,3的majority=2,5的majority=3,4的majority=2),2个哨兵都运行着,就可以允许执行故障转移。但是如果哨兵只有1个了,此时就没有majority来允许执行故障转移。
  • 两种数据丢失的情况
  1. 异步复制导致的数据丢失,因为master -> slave的复制是异步的,所以可能有部分数据还没复制到slave,master就宕机了。
  2. 脑裂导致的数据丢失。某个master所在机器突然脱离了正常的网络,跟其他slave机器不能连接,但是实际上master还运行着。此时哨兵可能就会认为master宕机了,然后开启选举,将其他slave切换成了master。这个时候,集群里就会有两个master,也就是所谓的脑裂。此时虽然某个slave被切换成了master,但是可能client还没来得及切换到新的master,还继续向旧master写数据。然后,旧master再次恢复的时候,会被作为一个slave挂到新的master上去,自己的数据会清空,重新从新的master复制数据。导致数据丢失。
  • 解决异步复制和脑裂导致的数据丢失
  1. min-slaves-to-write 1,防止脑裂导致的数据丢失
  2. min-slaves-max-lag 10,防止异步复制导致的数据丢失
    要求至少有1个slave,数据复制和同步的延迟不能超过10秒。如果说一旦所有的slave,数据复制和同步的延迟都超过了10秒钟,那么这个时候,master就不会再接收任何请求了
  • sdown和odown转换机制
  1. sdown达成的条件很简单,如果一个哨兵ping一个master,超过了is-master-down-after-milliseconds指定的毫秒数之后,就主观认为master宕机
  2. 如果一个哨兵在指定时间内,收到了quorum指定数量的其他哨兵也认为那个master是sdown了,那么就认为是odown了,客观认为master宕机
  • 哨兵集群的自动发现机制
  1. 哨兵互相之间的发现,是通过redis的pub/sub系统实现的
  2. 每个哨兵都会往__sentinel__:hello这个channel里发送一个消息,这时候所有其他哨兵都可以消费到这个消息,并感知到其他的哨兵的存在
  3. 每隔两秒钟,每个哨兵都会往自己监控的某个master+slaves对应的__sentinel__:hello channel里发送一个消息,内容是自己的host、ip和runid还有对这个master的监控配置
  4. 每个哨兵也会去监听自己监控的每个master+slaves对应的__sentinel__:hello channel,然后去感知到同样在监听这个master+slaves的其他哨兵的存在
  5. 每个哨兵还会跟其他哨兵交换对master的监控配置,互相进行监控配置的同步
  • slave配置的自动纠正
  1. 哨兵会负责自动纠正slave的一些配置,比如slave如果要成为潜在的master候选人,哨兵会确保slave在复制现有master的数据
  2. 如果slave连接到了一个错误的master上,比如故障转移之后,那么哨兵会确保它们连接到正确的master上
  • slave->master选举算法,如果一个master被认为odown了,而且majority哨兵都允许了主备切换,那么某个哨兵就会执行主备切换操作
  1. 跟master断开连接的时长、slave优先级、复制offset、run id
  2. 如果一个slave跟master断开连接已经超过了down-after-milliseconds的10倍,外加master宕机的时长,那么slave就被认为不适合选举为master,(down-after-milliseconds * 10) + milliseconds_since_master_is_in_SDOWN_state
  3. 按照slave优先级进行排序,slave priority越低,优先级就越高;如果slave priority相同,那么看replica offset,哪个slave复制了越多的数据,offset越靠后,优先级就越高;如果上面两个条件都相同,那么选择一个run id比较小的那个slave
  • quorum和majority
  1. 每次一个哨兵要做主备切换,首先需要quorum数量的哨兵认为odown
  2. 然后选举出一个哨兵来做切换,这个哨兵还得得到majority哨兵的授权,才能正式执行切换
  3. 如果quorum < majority,比如5个哨兵,majority就是3,quorum设置为2,那么就3个哨兵授权就可以执行切换;但是如果quorum >= majority,那么必须quorum数量的哨兵都授权,比如5个哨兵,quorum是5,那么必须5个哨兵都同意授权,才能执行切换
  • configuration epoch
  1. 执行切换的那个哨兵,会从要切换到的新master(salve->master)那里得到一个configuration epoch,这就是一个version号,每次切换的version号都必须是唯一的。
  2. 如果第一个选举出的哨兵切换失败了,那么其他哨兵,会等待failover-timeout时间,然后接替继续执行切换,此时会重新获取一个新的configuration epoch,作为新的version号
  • configuraiton传播
    哨兵完成切换之后,会在自己本地更新生成最新的master配置,然后同步给其他的哨兵,就是通过之前说的pub/sub消息机制。这里之前的version号就很重要了,因为各种消息都是通过一个channel去发布和监听的,所以一个哨兵完成一次新的切换之后,新的master配置是跟着新的version号的,其他的哨兵都是根据版本号的大小来更新自己的master配置的

redis cluster(多master + 读写分离 + 高可用)

  • 主要是针对海量数据+高并发+高可用的场景
  • redis单master架构的容量的瓶颈问题
    在这里插入图片描述
  • 图解redis cluster
  1. 自动将数据进行分片,每个master上放一部分数据
  2. 提供内置的高可用支持,部分master不可用时,还是可以继续工作的
  3. 在redis cluster架构下,每个redis要放开两个端口号,比如一个是6379,另外一个就是加10000的端口号,比如16379
  4. 16379端口号是用来进行节点间通信的,也就是cluster bus的东西,集群总线。cluster bus的通信,用来进行故障检测,配置更新,故障转移授权。cluster bus用了另外一种二进制的协议,主要用于节点间进行高效的数据交换,占用更少的网络带宽和处理时间
    在这里插入图片描述
  • hash算法 -> 一致性hash算法(memcached) -> redis cluster,hash slot算法
  1. 最老土的hash算法和弊端(大量缓存重建)
    在这里插入图片描述
  2. 一致性hash算法(自动缓存迁移)+虚拟节点(自动负载均衡)
    在这里插入图片描述
    在这里插入图片描述
  3. redis cluster的hash slot算法
  • redis cluster有固定的16384个hash slot,对每个key计算CRC16值,然后对16384取模,可以获取key对应的hash slot
  • redis cluster中每个master都会持有部分slot
  • hash slot让node的增加和移除很简单,增加一个master,就将其他master的hash slot移动部分过去,减少一个master,就将它的hash slot移动到其他master上去
  • 移动hash slot的成本是非常低的
  • 客户端的api,可以对指定的数据,让他们走同一个hash slot,通过hash tag来实现
    在这里插入图片描述

节点间的内部通信机制

  1. 基础通信原理
  • redis cluster节点间采取gossip协议进行通信,跟集中式不同,不是将集群元数据(节点信息,故障,等等)集中存储在某个节点上,而是互相之间不断通信,保持整个集群所有节点的数据是完整的。
  • 集中式:好处在于,元数据的更新和读取,时效性非常好,一旦元数据出现了变更,立即就更新到集中式的存储中,其他节点读取的时候立即就可以感知到; 不好在于,所有的元数据的跟新压力全部集中在一个地方,可能会导致元数据的存储有压力
  • gossip:好处在于,元数据的更新比较分散,不是集中在一个地方,更新请求会陆陆续续,打到所有节点上去更新,有一定的延时,降低了压力; 缺点,元数据更新有延时,可能导致集群的一些操作会有一些滞后
  • 10000端口,每个节点都有一个专门用于节点间通信的端口。每个节点每隔一段时间都会往另外几个节点发送ping消息,同时其他几个节点接收到ping之后返回pong
  • 交换的信息,故障信息,节点的增加和移除,hash slot信息,等等
  1. gossip协议
  • gossip协议包含多种消息,包括ping,pong,meet,fail,等等
  • meet: 某个节点发送meet给新加入的节点,让新节点加入集群中,然后新节点就会开始与其他节点进行通信。redis-trib.rb add-node,其实内部就是发送了一个gossip meet消息,给新加入的节点,通知那个节点去加入我们的集群
  • ping: 每个节点都会频繁给其他节点发送ping,其中包含自己的状态还有自己维护的集群元数据,互相通过ping交换元数据
  • pong: 返回ping和meet,包含自己的状态和其他信息,也可以用于信息广播和更新
  • fail: 某个节点判断另一个节点fail之后,就发送fail给其他节点,通知其他节点,指定的节点宕机了
  1. ping消息深入
  • ping很频繁,而且要携带一些元数据,所以可能会加重网络负担
  • 每个节点每秒会执行10次ping,每次会选择5个最久没有通信的其他节点
  • 当然如果发现某个节点通信延时达到了cluster_node_timeout / 2,那么立即发送ping,避免数据交换延时过长
  • 每次ping,一个是带上自己节点的信息,还有就是带上1/10其他节点的信息,发送出去,进行数据交换。至少包含3个其他节点的信息,最多包含总节点-2个其他节点的信息

面向集群的jedis内部实现原理

  1. 基于重定向的客户端
  • 请求重定向,客户端可能会挑选任意一个redis实例去发送命令,每个redis实例接收到命令,都会计算key对应的hash slot。如果在本地就在本地处理,否则返回moved给客户端,让客户端进行重定向。
    cluster keyslot mykey,可以查看一个key对应的hash slot是什么。
    用redis-cli的时候,可以加入-c参数,支持自动的请求重定向,redis-cli接收到moved之后,会自动重定向到对应的节点执行命令
  • 计算hash slot,就是根据key计算CRC16值,然后对16384取模,拿到对应的hash slot。
    用hash tag可以手动指定key对应的slot,同一个hash tag下的key,都会在一个hash slot中,比如set mykey1:{100}和set mykey2:{100}
  • hash slot查找,节点间通过gossip协议进行数据交换,就知道每个hash slot在哪个节点上
  1. smart jedis
  • 什么是smart jedis
    本地维护一份hashslot -> node的映射表,缓存,大部分情况下,直接走本地缓存就可以找到hashslot -> node,不需要通过节点进行moved重定向
  • JedisCluster的工作原理
    在JedisCluster初始化的时候,就会随机选择一个node,初始化hashslot -> node映射表,同时为每个节点创建一个JedisPool连接池
    每次基于JedisCluster执行操作,首先JedisCluster都会在本地计算key的hashslot,然后在本地映射表找到对应的节点
    如果那个node正好还是持有那个hashslot,那么就ok; 如果说进行了reshard这样的操作,可能hashslot已经不在那个node上了,就会返回moved
    如果JedisCluter API发现对应的节点返回moved,那么利用该节点的元数据,更新本地的hashslot -> node映射表缓存
    重复上面几个步骤,直到找到对应的节点,如果重试超过5次,那么就报错,JedisClusterMaxRedirectionException
    jedis老版本,可能会出现在集群某个节点故障还没完成自动切换恢复时,频繁更新hash slot,频繁ping节点检查活跃,导致大量网络IO开销
    jedis最新版本,对于这些过度的hash slot更新和ping,都进行了优化,避免了类似问题
  • hashslot迁移和ask重定向
    如果hash slot正在迁移,那么会返回ask重定向给jedis
    jedis接收到ask重定向之后,会重新定位到目标节点去执行,但是因为ask发生在hash slot迁移过程中,所以JedisCluster API收到ask是不会更新hashslot本地缓存
    已经可以确定说,hashslot已经迁移完了,moved是会更新本地hashslot->node映射表缓存的

高可用性与主备切换原理

  1. 判断节点宕机
  • 如果一个节点认为另外一个节点宕机,那么就是pfail,主观宕机。在cluster-node-timeout内,某个节点一直没有返回pong,那么就被认为pfail
  • 如果多个节点都认为另外一个节点宕机了,那么就是fail,客观宕机,跟哨兵的原理几乎一样。如果一个节点认为某个节点pfail了,那么会在gossip ping消息中,ping给其他节点,如果超过半数的节点都认为pfail了,那么就会变成fail
  1. 从节点过滤
  • 对宕机的master node,从其所有的slave node中,选择一个切换成master node
  • 检查每个slave node与master node断开连接的时间,如果超过了cluster-node-timeout * cluster-slave-validity-factor,那么就没有资格切换成master
  1. 从节点选举
  • 每个从节点,都根据自己对master复制数据的offset,来设置一个选举时间,offset越大(复制数据越多)的从节点,选举时间越靠前,优先进行选举
  • 所有的master node开始slave选举投票,给要进行选举的slave进行投票,如果大部分master node(N/2 + 1)都投票给了某个从节点,那么选举通过,那个从节点可以切换成master
  • 从节点执行主备切换,从节点切换为主节点
  1. 与哨兵比较,整个流程跟哨兵相比,非常类似,所以说,redis cluster功能强大,直接集成了replication和sentinal的功能
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值