1、redis主从架构
? ? ? ? redis的主从架构指的是,为redis主节点部署一个或多个从节点,让从节点连接到主节点,从主节点上复制数据,让从节点作为主节点的数据备份,并且实现读写分离,主节点负责读和写,从节点只负责读,既实现了数据备份,又分担了主节点的压力,提高了redis服务的性能。
? ? ? ? redis主从复制方式有全量同步和增量同步。当执行全量同步时,主节点保存当前内存中的数据到rdb文件中,同时缓存收到的写操作到内存中,当rdb文件保存完成,将这个rdb文件传给从节点,从节点将rdb文件读取到内存中,读取完成向主节点通知成功,然后主节点再将此期间缓存的写命令通过redis协议传输给从节点,从节点在本地执行这些命令,执行完毕就完成了一次全量同步。增量同步:主节点会为每个从节点保存一份同步日志和同步标识,当执行增量同步时,从节点会将自己的同步标识和同步偏移量发送给主节点,主节点通过同步标识找到同步文件,查找从节点携带的同步偏移量是否还在同步日志中,如果还在,就从这个同步偏移量开始,传递后续的命令给从节点执行,如果从节点携带的同步偏移量已经不在同步日志中了,那就执行一次全量同步。同步偏移量在同步日志中丢失的原因可能为:从上一次主从同步之后,此主从之间的连接断开过,并且断开的时间比较长;或者是从上一次主从同步之后,主节点收到了太多写操作。redis会在主从初次建立连接的时候执行一次全量同步,后续只要能执行增量同步就执行增量同步,增量同步不成功才全量同步,不过从节点在任意时刻都可以主动发起一次全量同步。
? ? ? ? 需要注意的是,主从架构只是实现了主节点的数据备份和redis服务的吞吐量的提升,并不能保证服务的高可用,因为一旦主节点宕机了,就会导致redis服务不可用,若要保证redis主从的高可用,可以部署sentinel哨兵队列。
2、redis主从与redis sentinel
? ? ? ? sentinel的作用就是去监控redis主从节点的状态,它会周期性地ping主节点和各个从节点,当主节点ping不通时,会判定主节点下线,然后去提升某个从节点为新的主节点来继续提供服务,保证redis服务的高可用。当某个从节点ping不通时,会判定此从节点下线,后续的读请求将不会再到达此从节点。
? ? ? ? 但实际上主节点不应该被如此轻易地判定为下线,因为存在误判的概率,当sentinel ping不通主节点是因为sentinel与主节点之间的网络波动引起的时,这种误判会导致集群脑裂问题出现【也就是集群中出现了两个主节点】。所以为了降低误判概率,sentinel应该部署多实例,并且设置当有过半数的sentinel实例ping不通主节点时才判定主节点下线,这样能够降低误判的概率。
? ? ? ? sentinel提升新的主节点的过程:
? ? ? ? 当sentinel判定主节点下线后,会提升某个从节点作为新的主节点,这个从节点不是随机选择的,要有如下考量:1、从库优先级,这个优先级是我们通过slave-priority配置项设置的,我们一般会选择一个硬件配置最高的从节点设置为具有最高优先级;2、应该选择那个数据同步进度最靠前的从节点,以缩小数据丢失的范围。
? ? ? ? 当新的主节点被选出来之后,sentinel会向各个从节点发通知,让它们与新的主节点建立主从关系,并建立连接,进行一次主从的全量同步;然后还要将新的主节点信息通知给各个客户端,以便让各个客户端去连接新的主节点执行写操作。
3、redis cluster
? ? ? ? 当redis内存不够用时,我们可以去部署redis cluster,真正的redis集群,redis集群一般是由多组redis主从组成的集群,集群将整个的内存分成16384个哈希槽,这16384个哈希槽均匀地分散在各个主节点上存储,每个主节点负责存储一段或多段连续的哈希槽,数据是存储在哈希槽上的,set、get操作都是在哈希槽上执行的,而哈希槽只存储在主节点上,所以集群中只有主节点参与数据的存取,为每个主节点部署从节点是为了保证redis集群的高可用,如果不设置从节点,一旦某个主节点宕机了,集群就会因为保存在这个主节点上的哈希槽的缺失而无法继续提供服务。所以要为集群中的每个主节点设置从节点,从节点会复制主节点的数据,一旦某个主节点宕机了,集群会感知到,然后执行自动的主备切换,将宕机那个主节点的从节点提升为主节点,将之前主节点上保存的哈希槽移动到新的主节点上来,让新的主节点继续参与数据存取。
? ? ? ? 当我们在spring boot项目中使用redis时需要注意,spring-boot-data-redis默认使用lettuce作为redis连接池,而lettuce不会感知到redis集群节点信息的变化,而我们如果去修改配置重启应用又不利于保证系统的高可用,所以我们可以不用lettuce,在spring-boot-data-redis中排除掉lettuce依赖,然后引入jedis作为redis客户端来使用。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.3.12.RELEASE</version>
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>