《我想进大厂》之Redis夺命连环11问( 三 )

如果不设置 , 默认选项将会是everysec , 因为always来说虽然最安全(只会丢失一次事件循环的写命令) , 但是性能较差 , 而everysec模式只不过会可能丢失1秒钟的数据 , 而no模式的效率和everysec相仿 , 但是会丢失上次同步AOF文件之后的所有写命令数据 。
怎么实现Redis的高可用?要想实现高可用 , 一台机器肯定是不够的 , 而redis要保证高可用 , 有2个可选方案 。
主从架构主从模式是最简单的实现高可用的方案 , 核心就是主从同步 。 主从同步的原理如下:

  1. slave发送sync命令到master
  2. master收到sync之后 , 执行bgsave , 生成RDB全量文件
  3. master把slave的写命令记录到缓存
  4. bgsave执行完毕之后 , 发送RDB文件到slave , slave执行
  5. master发送缓存中的写命令到slave , slave执行

《我想进大厂》之Redis夺命连环11问文章插图
这里我写的这个命令是sync , 但是在redis2.8版本之后已经使用psync来替代sync了 , 原因是sync命令非常消耗系统资源 , 而psync的效率更高 。
哨兵基于主从方案的缺点还是很明显的 , 假设master宕机 , 那么就不能写入数据 , 那么slave也就失去了作用 , 整个架构就不可用了 , 除非你手动切换 , 主要原因就是因为没有自动故障转移机制 。 而哨兵(sentinel)的功能比单纯的主从架构全面的多了 , 它具备自动故障转移、集群监控、消息通知等功能 。
《我想进大厂》之Redis夺命连环11问文章插图
哨兵可以同时监视多个主从服务器 , 并且在被监视的master下线时 , 自动将某个slave提升为master , 然后由新的master继续接收命令 。 整个过程如下:
  1. 初始化sentinel , 将普通的redis代码替换成sentinel专用代码
  2. 初始化masters字典和服务器信息 , 服务器信息主要保存ip:port , 并记录实例的地址和ID
  3. 创建和master的两个连接 , 命令连接和订阅连接 , 并且订阅sentinel:hello频道
  4. 每隔10秒向master发送info命令 , 获取master和它下面所有slave的当前信息
  5. 当发现master有新的slave之后 , sentinel和新的slave同样建立两个连接 , 同时每个10秒发送info命令 , 更新master信息
  6. sentinel每隔1秒向所有服务器发送ping命令 , 如果某台服务器在配置的响应时间内连续返回无效回复 , 将会被标记为下线状态
  7. 选举出领头sentinel , 领头sentinel需要半数以上的sentinel同意
  8. 领头sentinel从已下线的的master所有slave中挑选一个 , 将其转换为master
  9. 让所有的slave改为从新的master复制数据
  10. 将原来的master设置为新的master的从服务器 , 当原来master重新回复连接时 , 就变成了新master的从服务器
sentinel会每隔1秒向所有实例(包括主从服务器和其他sentinel)发送ping命令 , 并且根据回复判断是否已经下线 , 这种方式叫做主观下线 。 当判断为主观下线时 , 就会向其他监视的sentinel询问 , 如果超过半数的投票认为已经是下线状态 , 则会标记为客观下线状态 , 同时触发故障转移 。
能说说redis集群的原理吗?如果说依靠哨兵可以实现redis的高可用 , 如果还想在支持高并发同时容纳海量的数据 , 那就需要redis集群 。 redis集群是redis提供的分布式数据存储方案 , 集群通过数据分片sharding来进行数据的共享 , 同时提供复制和故障转移的功能 。
节点一个redis集群由多个节点node组成 , 而多个node之间通过cluster meet命令来进行连接 , 节点的握手过程:
  1. 节点A收到客户端的cluster meet命令
  2. A根据收到的IP地址和端口号 , 向B发送一条meet消息
  3. 节点B收到meet消息返回pong
  4. A知道B收到了meet消息 , 返回一条ping消息 , 握手成功
  5. 最后 , 节点A将会通过gossip协议把节点B的信息传播给集群中的其他节点 , 其他节点也将和B进行握手

《我想进大厂》之Redis夺命连环11问文章插图
槽slotredis通过集群分片的形式来保存数据 , 整个集群数据库被分为16384个slot , 集群中的每个节点可以处理0-16384个slot , 当数据库16384个slot都有节点在处理时 , 集群处于上线状态 , 反之只要有一个slot没有得到处理都会处理下线状态 。 通过cluster addslots命令可以将slot指派给对应节点处理 。
slot是一个位数组 , 数组的长度是16384/8=2048 , 而数组的每一位用1表示被节点处理 , 0表示不处理 , 如图所示的话表示A节点处理0-7的slot 。