高可用篇
# 主从
# 全量同步
主从第一次建立连接时,会执行全量同步,将master节点的所有数据都拷贝给slave节点
如何知道是第一次连接:
Replication Id:数据集的标记,id一致则说明是同一数据集。每一个master都有唯一的replid,slave则会继承master节点的replid
offset:偏移量,随着记录在repl_baklog中的数据增多而逐渐增大。slave完成同步时也会记录当前同步的offset。如果slave的offset小于master的offset,说明slave数据落后于master,需要更新
replid不一致,就是第一次连接,直接全量同步
slave节点请求增量同步
master节点判断replid,发现不一致,拒绝增量同步
master将完整内存数据生成RDB(bgsave),发送RDB到slave
主服务器生成这个RDB不会阻塞主线程,bgsave命令会产生一个子进程来生成RDB文件,异步工作;
slave清空本地数据,加载master的RDB
master将RDB期间的命令记录在repl_baklog,并持续将log中的命令发送给slave
slave执行接收到的命令,保持与master之间的同步
master将完整内存数据生成RDB,发送RDB到slave。后续命令则记录在repl_baklog,逐个发送给slave;
简单来说三阶段:
- 建立链接,协商同步;
- 主服务器同步数据给从服务器;
- 主服务器发送新写操作命令给从服务器;
# 增量同步
全量同步需要先做RDB,然后将RDB文件通过网络传输个slave,成本太高了。因此除了第一次做全量同步,其它大多数时候slave与master都是做增量同步
slave提交自己的offset到master,master获取repl_baklog中从offset之后的命令给slave
# repl_backlog原理
这个文件是个固定大小的环形数组,repl_baklog中会记录Redis处理过的命令日志及offset,包括master当前的offset,和slave已经拷贝到的offset,差异不超过环大小增量同步,超过全量同步
# 主从同步优化(优化Redis主从集群)
- 在master中配置repl-diskless-sync yes启用无磁盘复制,避免全量同步时的磁盘IO。写入网络IO流
- Redis单节点上的内存占用不要太大,减少RDB导致的过多磁盘IO
- 适当提高repl_baklog的大小,发现slave宕机时尽快实现故障恢复,尽可能避免全量同步
- 限制一个master上的slave节点数量,如果实在是太多slave,则可以采用主-从-从链式结构,减少master压力
# 过期key如何处理?
主节点处理了一个key或者通过淘汰算法淘汰了一个key,这个时间主节点模拟一条del命令发送给从节点,从节点收到该命令后,就进行删除key的操作。
# 同步复制还是异步复制?
Redis 主节点每次收到写命令之后,先写到内部的缓冲区,然后异步发送给从节点。
# 主从数据不一致
主从节点间的命令复制是异步的,无法实现强一致性;
主节点执行完命令后,直接把结果返回给客户端了,并没有等从节点页执行完。
如果从节点还没有执行主节点同步过来的命令,主从节点间的数据就不一致了;
如何解决?
- 保证主从节点在同一个机房,网络良好;
- 外部程序来监控主从节点间的复制进度;
# 哨兵
作用:主从节点故障转移;
如何工作?
监控
基于心跳检测机制;
- 主观下线:如果某sentinel节点发现某实例未在规定时间响应,则认为该实例主观下线
- 客观下线:若超过指定数量(quorum)的sentinel都认为该实例主观下线,则该实例客观下线。quorum值最好超过Sentinel实例数量的一半
选主
- 首先会判断slave节点与master节点断开时间长短,如果超过指定值(down-after-milliseconds * 10)则会排除该slave节点(说明断开时间最长,丢失数据最多)
- 然后判断slave节点的slave-priority值,越小优先级越高,如果是0则永不参与选举
- 如果slave-prority一样,则判断slave节点的offset值,越大说明数据越新,优先级越高
- 最后是判断slave节点的运行id大小,越小优先级越高
故障转移
sentinel给备选的slave1节点发送slaveof no one命令,让该节点成为master
sentinel给所有其它slave发送slaveof 192.168.150.101 7002 命令,让这些slave成为新master的从节点,开始从新的master上同步数据。
最后,sentinel将故障节点标记为slave,当故障节点恢复后会自动成为新的master的slave节点
# 集群
解决问题:
- 海量数据存储问题
- 高并发写的问题
主节点之间互为哨兵,自动实现故障转移
# 散列插槽
为什么不用哈希?
通过key计算hash值,根据节点数量求模,增加节点影响缓存命中率(重新分配代价太高);
Redis会把每一个master节点映射到0~16383共16384个插槽(hash slot)上,数据key不是与节点绑定,而是与插槽绑定。redis会根据key的有效部分计算插槽值,分两种情况:
- key中包含"{}",且“{}”中至少包含1个字符,“{}”中的部分是有效部分
- key中不包含“{}”,整个key都是有效部分
key是num,那么就根据num计算,如果是{itcast}num,则根据itcast计算。计算方式是利用CRC16算法得到一个hash值,然后对16384取余,得到的结果就是slot值
Redis如何判断某个key应该在哪个实例?
- 将16384个插槽分配到不同的实例
- 根据key的有效部分计算哈希值,对16384取余
- 余数作为插槽,寻找插槽所在实例即可
如何将同一类数据固定的保存在同一个Redis实例?
这一类数据使用相同的有效部分,例如key都以{typeId}为前缀
set num 123
set {a}num 111
# 一致性哈希
将哈希值映射到环上0-2^32-1,将节点配置到环上(根据ip或机器名求哈希值),所有的数据顺时针找到其对应节点;
如果节点不均衡?
新增节点?
影响的数据为:节点B到节点E之间数据,只会分担节点C的压力;
解决方案:虚拟节点,分担的压力1/n
存在什么问题?
需要找到比它值大的节点,查找数据所需时间增加(相比简单hash);
集群节点调整时可用性降低(数据迁移);