Redis的集群模式:主从 & 哨兵 & 分片集群

  • 基于Redis集群解决单机Redis存在的问题,在之前学Redis一直都是单节点部署
  • 宕机:该节点实例与其它实例失去连接

Redis有哪些部署方案? 

单机版:单机部署,单机Redis能够承载的QPS大概就在上万到几万不等,这种部署方式很少使用,存在的问题:

  • 内存容量有限
  • 处理能力有限
  • 无法高可用

单机或单节点Redis存在的四大问题:

  • 数据丢失问题:Redis是内存存储,服务重启可能会丢失数据  =>  利用Redis数据持久化的功能将数据写入磁盘
  • 并发能力问题:单节点Redis并发能力虽然不错,但也无法满足如618这样的高并发场景  =>  搭建一主多从集群,实现读写分离
  • 单点故障 - 故障恢复问题:如果Redis宕机,则服务不可用,需要一种自动的故障恢复手段  =>  利用Redis哨兵,实现健康检测和自动故障恢复
  • 存储能力问题:Redis基于内存存储,单节点能存储的数据量难以满足海量数据要求  =>  搭建分片集群,利用插槽机制实现动态扩容,从理论上来讲,它的存储能力是没有上限的

介绍一下Redis的集群模式?

  • Redis有三种主要的集群模式,用于在分布式环境中实现高可用性和数据复制,这些集群模式分别是:主从复制(Master-Slave Replication)、哨兵模式(Sentinel)和Redis Cluster模式。

Redis集群的目的是什么?

  • Redis集群可以将数据分割到不同的服务器节点上,当Redis集群中的部分节点宕机或不可用时,Redis其它节点仍然能够继续处理命令,不至于整个Redis集群不可用,用来保证可用性。

Redis集群如何选择数据库?

  • Redis集群目前无法做数据库选择,默认在0号数据库。

Redis集群最少需要几个主节点?

  • Redis集群正常工作,至少需要3个主节点。 

1. Redis主从

  • 搭建主从架构
  • 主从数据同步原理

单节点Redis的并发能力是有上限的,要进一步提高Redis的并发能力,就需要搭建主从集群,实现读写分离。

主从模式简介

一主多从

  • 主从复制是Redis最简单的集群模式,这个模式主要是为了解决单点故障的问题,所以将数据复制到多个副本中,这样即使有一台服务器出现故障,其它服务器依然可以继续提供服务。
  • 主从复制模式中,包括一个主节点(Master)和一个或多个从节点(Slave),主节点负责处理所有写操作和读操作,而从节点则负责复制主节点的数据,并且只能处理读操作,当主节点发送故障时,可以将一个从节点升级为主节点,实现故障转移(需要手动实现)。

1.1.主从集群结构

  • Redis的集群往往都是主从集群,它往往会有一个Master主节点,多个Slave / Replica从节点。 

下图就是一个简单的Redis主从集群结构:

如图所示,集群中有一个Master主节点、两个Slave从节点(现在叫Replica) =>  起码要包含三个节点,要有三个Redis实例,一主两从

  • 在Redis 5.0以前,从节点是叫Slave的,后来改名叫Replica  =>  都是代表从节点 

当我们通过Redis的Java客户端访问主从集群时,应该做好路由:

  • 如果是写操作,应该访问Master主节点,Master主节点会自动将数据同步给两个Slave从节点

  • 如果是读操作,建议访问各个Slave从节点,从而分担并发压力

Master主节点可以执行set命令(写操作),Replica从节点只能执行get命令(读操作) 。

为什么Redis要做成这种主从的集群,而不是传统的负载均衡集群呢?

  • 这是因为Redis应用当中大多数都是读多写少的场景,也就是查询比较多,而增删改比较少,既然如此,我们更多要应对的是读的压力,那我做了主从以后,我们还可以去做读写分离, 也就是说,我在执行写操作时,我让它去访问Master主节点,但如果执行的是读操作,那我就把你的请求分发到各个Slave或Replica从节点,这样我们一主多从,多个从节点共同承担读的请求,我们的读并发能力就可以得到一个比较大的提升,所以这就是为什么要搭建主从集群的一个原因了。
  • 但是做主从集群,必须保证一点,就是客户端在读取的时候,不管访问到哪个Slave从节点,都必须要保证拿到相同的结果     =>    如何保证?  需要让Master主节点把它上面的数据同步给每一个Slave从节点,这就是Redis主从架构的一个基本模式了

1.2 搭建主从集群 

1. 准备实例和配置  
  • 我们会在同一台虚拟机中开启3个Redis实例,模拟主从集群。  
  • 我们会在同一个虚拟机中利用3个Docker容器来搭建主从集群。

  • 要在同一台虚拟机开启3个实例,必须准备三份不同的配置文件和目录,配置文件所在目录也就是工作目录。

  • 在同一个机器下还要修改每个实例的端口

2. 启动 & 开启主从关系

分别启动多个Redis实例虽然我们启动了3个Redis实例,但是它们并没有形成主从关系,我们需要通过命令来配置主从关系:

# Redis5.0以前
slaveof <masterip> <masterport>
# Redis5.0以后
replicaof <masterip> <masterport>
有临时和永久两种模式:
  • 永久生效:在redis.conf文件中利用slaveof命令指定Master主节点的IP和端口

  • 临时生效:直接利用redis-cli控制台输入slaveof命令,并且指定Master主节点的IP和端口

INFO replication:查看集群的状态信息

这样,就可以实现读写分离了,如果在Slave从节点上执行set写操作,会报错:

假设有A、B两个Redis实例,如何让B作为A的Slave从节点?

  • 在B节点执行命令:slaveof      A的IP     A的Port端口 

1.3 数据同步原理 

  • Redis主从同步的底层工作原理 

Redis集群主从复制采用的是异步复制机制。 

1. 全量同步

  • 主从第一次建立连接时,会执行全量同步  =>  主从第一次同步是全量同步,将Master主节点的所有数据都拷贝给Replica从节点,流程:

  • 将来基于数据版本可以做一个控制

首先,从节点通过replicaof命令与主节点建立连接,请求数据同步,主节点会判断是否为第一次连接,如果是,则将完整数据发送给Slave从节点,Master主节点使用RDB持久化技术,将内存中的数据生成RDB文件,并在后台发送给Slave从节点,Slave清空本地数据,加载Master主节点的RDB,这样就能确保Slave节点与Master节点的数据基本一致了。 

为什么是基本一致而不是完全一致呢? 
  • bgsave是异步执行的,在执行的过程当中,Matser节点的主进程还会去处理用户的请求,也就是说会有新的数据写入,新写入的数据并没有发给Slave从节点,所以Master节点的主进程除了处理这些新的数据以外,它还会把这些新写入的数据命令记录到repl_baklog的这样一个缓冲区当中,它是一个内存的缓冲区,repl_backlog里面记录的就是RDB期间收到的一些新的命令,并持续将log中的命令发送给Slave,repl_baklog里面记录的所有命令 + RDB文件里面的数据合在一起就是我们Master节点上的完整数据了。 

.........Slave执行接收到的命令,保持与Master之间的同步,这样就能保证Slave节点与Master节点上面的数据完全一致了。 

为什么叫全量同步呢?
  • 因为它有一个RDB的过程,它会把内存形成快照,整体发送给Slave,所以叫全量同步,全量同步是比较消耗性能的,因为生成RDB文件的速度比较慢,这种同步只有在第一次建立连接时才会去做。 
这里有一个问题,Master主节点如何得到Slave从节点是否是第一次来同步呢?Master如何判断Slave节点是不是第一次来做数据同步?  
  • 这里会用到两个很重要的概念作为判断依据:
  1. Replication Id 简称replid,是数据集的标记,replid一致则说明是同一数据集;每一个Master都有自己唯一的replid,Slave节点则会继承Master节点的replid。
  2. offset偏移量,记录已同步的数据量,它会随着记录在repl_baklog中的数据增多而逐渐增大;Slave完成同步时也会记录当前同步的offset,Slave的offset一定是小于等于Master的offset,如果Slave的offset小于Matser的offset,则说明Slave数据落后于Master,需要更新。

因此,Slave做数据同步,必须向Master声明自己的replication idoffset,Master才可以判断到底需要同步哪些数据,基于offset去判断数据同步的进度。 

由于我们在执行slaveof命令之前,所有Redis节点都是Master,即每个Slave在成为Slave之前,都有自己的replid和offset,当我们第一次执行slaveof命令,与Master建立主从关系时,发送的replid和offset是自己的,与Master肯定不一致,在数据同步请求时,Master判断Slave发送来的replid与自己的ID是否一致,如果发现不一致,则说明是第一次同步,说明这是一个全新的Slave,此时就知道要做全量同步了,主节点Master会将自己的replid和offset都发送给这个Slave,Slave保存这些信息到本地,并且Slave将Master的replid作为自己的ID,自此以后Slave的replid就与Matser一致了。

因此,Master判断一个节点是否是第一次同步的依据,就是看replid是否一致,流程如图:

全量同步的完整流程描述:

  • Slave从节点执行replicaof命令与Master主节点建立连接,并尝试请求增量同步
  • Master主节点判断replid,发现不一致,拒绝增量同步,开始进行全量同步 => Full resync
  • Master将完整内存数据生成RDB,发送RDB文件到Slave
  • Slave清空本地数据(Flushing old data  =>  清空本地旧数据),加载接收到的Master所发送的RDB文件
  • Master将RDB期间的命令记录在repl_baklog,并持续将log中的命令发送给Slave
  • Slave执行接收到的命令,保持与Master之间的同步

2. 增量同步 

  • 主从第一次同步是全量同步,但如果Slave重启后同步(不管是因为故障重启还是自己重启),则执行增量同步 
  • 全量同步需要先做RDB,然后将RDB文件通过网络传输给Slave,成本太高了,因此除了第一次做全量同步,其它大多数时候Slave与Master都是做增量同步
什么是增量同步?
  • 就是只更新Slave与Master存在差异的部分数据  =>  差异的这一部分数据就是Slave在宕机期间错过的那些数据,增量同步是在主从服务器数据存在差异时进行的一种同步方式。 

增量同步是在Slave从服务器或者Slave从节点已经进行过全量同步后,Master主节点和Slave从节点之间的数据存在差异时进行的同步操作。 

  • Master主服务器从上次Slave同步的offset位置开始发送增量数据给Slave从服务器。 
那么Master怎么知道Slave与自己的数据差异在哪里呢?  =>  repl_baklog原理
  • 这就要说到全量同步时的repl-baklog文件了,这个文件的本质是一个固定大小的数组,只不过数组是环形,也就是说角标达到数组末尾后,会再次从0开始读写,这样数组头部的数据就会被覆盖。 

repl_baklog中记录的就是Master当前的offset和Slave已经拷贝到的offset:

要增量同步拷贝的就是Slave的offset到Master最新的offset之间的这一部分数据所以repl_baklog的本质就是Slave与Master它们数据差异的一个缓冲区,repl_baklog只要记录了Slave尚未同步的这部分数据就ok了, 只要Slave与Master之间的数据差距别超过这个环的存储上限,那你永远能够从这个环里找到你所需要的数据,这样就永远能实现增量同步,但如果你Slave与Master之间的差距太多,已经超过了这个存储上限了,那么这个时候就没有办法做增量同步了。

也就是说,如果Slave出现网络阻塞,导致Master的offset远远超过了Slave的offset,如果Master持续写入新的数据,Master的offset就会覆盖repl_baklog中旧的数据,直到将Slave现在的offset也覆盖,此时如果Slave恢复,需要同步,却发现自己的offset都没有了,即Slave的offeset被覆盖,无法找到对应的offset,也就无法完成增量同步了,则此时只能做全量同步:


棕色框中的红色部分,就是尚未同步,但是却已经被覆盖的数据。
结论:
  • repl_baklog大小有上限,写满后会覆盖最早的数据,如果Slave断开时间过久,导致尚未备份的数据被覆盖,则无法基于repl_baklog做增量同步,只能再次全量同步   =>  只能尽可能的避免。

3. 主从同步优化

  • 主从同步可以保证主从数据的一致性,非常重要。 
可以从以下几个方面来优化Redis主从集群:

一方面是尽可能的去减少全量同步,因为全量同步它的性能比较差,另一方面要去优化这个全量同步的性能 

  • 在Master中配置repl-diskless-sync  yes启用无磁盘复制(就是当我去写RDB文件时,我不把它写到磁盘的IO流了,而是写到网络当中直接发给Slave,减少了一次磁盘读写,性能就提高了很多),避免全量同步时的磁盘IO    =>   使用场景:你的磁盘比较慢,而网络却非常的快,如果你网络带宽不够,此时反而可能会导致网络阻塞
  • Redis单节点上的内存占用不要太大,减少RDB导致的过多磁盘IO以及网络IO
  • 适当提高repl_baklog的大小,发现Slave宕机时尽快实现故障恢复,尽可能避免全量同步
  • 限制一个Master上的Slave节点数量,如果实在是太多Slave,则可以采用主-从-从链式结构,减少Master压力  =>  让两个Slave从节点也去 

主-从-从链式结构是一种用于减轻主节点同步压力的方法。在这种结构中,一个主节点为主,两个从节点为从,再由这两个从节点为主,各自再带领两个从节点,这样,主节点可以将同步任务交给这两个中间的从节点,从而减少自己的压力。

主-从-从架构图

简述全量同步和增量同步的区别?

  • 全量同步:Master将完整内存数据生成RDB,发送RDB到Slave,后续命令则记录在repl_baklog,逐个发送给Slave
  • 增量同步:Slave提交自己的offset到Master,Master获取repl_baklog中从offset之后的命令给Slave 

什么时候执行全量同步?

  • Slave节点第一次连接Master节点时
  • Slave节点断开时间太久,repl_baklog中的offset已经被覆盖时

什么时候执行增量同步?

  • Slave节点断开又恢复,并且在repl_baklog中能找到offset时

主从复制的优缺点总结:

  • 主从复制的优势在于简单易于,适用于读多写少的场景,它提供了数据备份功能,并且有很好的扩展性,只要增加更多的从节点,就能让整个集群的读的能力不断提升。
  • 但是主从模式最大的缺点,就是不具备故障自动转移的能力,没有办法做容错和恢复
  • 主节点和从节点的宕机都会导致客户端部分读写请求失败,需要人工介入让节点恢复或者手动切换一台从节点服务器变成主节点服务器才可以,并且在主节点宕机时,如果数据没有及时复制到从节点,也会导致数据的不一致。 

为了解决主从模式的无法自动容错及恢复的问题,Redis引入了一种哨兵模式的集群架构,从而来保证主从集群的高可用。

2. Redis哨兵 

哨兵模式简介

  • 哨兵模式是在主从复制的基础上加入了哨兵节点,哨兵节点是一种特殊的Redis节点,用于监控主节点和从节点的状态,当主节点发生故障时,哨兵节点可以自动进行故障转移,Master节点挂掉后,哨兵进程会主动选举新的Master,会选择一个合适的从节点升级为主节点,并通知其它的从节点和应用程序进行更新。
  • 哨兵模式:可用性高,但是每个节点存储的数据是一样的,浪费内存空间
  • 适用场景:数据量不是很多,集群规模不是很大,需要自动容错容灾的时候使用。

在原来的架构中,引入哨兵节点,其作用是监控Redis主节点和从节点的状态,每个Redis实例都可以作为哨兵节点,通常需要部署多个哨兵节点(Sentinel也是需要集群部署),以确保故障转移的可靠性。  

哨兵工作机制

  • Redis提供了哨兵(Sentinel)机制来监控主从集群的健康状态,实现主从集群的自动故障恢复,确保集群的高可用性。

哨兵的作用:

哨兵集群作用原理图
Sentinel哨兵的作用如下:
  • 集群状态监控:Sentinel会不断检查Master和Slave是否预期工作
  • 自动故障恢复或故障转移(failover - 故障转移或容错):如果Master故障,Sentinel会将一个Slave提升为Master,当故障实例恢复后会成为Slave,会以新的Master为主
  • 状态通知:Sentinel哨兵节点充当Redis客户端的服务发现来源,哨兵节点通过发布订阅功能来通过客户端有关主节点状态变化的消息,当集群发送failover故障转移时,Sentinel会将最新集群消息推送给Redis的客户端,客户端收到消息后,会更新配置,将新的主节点信息应用于连接池,从而使客户端可以继续与新的主节点进行交互 

Sentinel哨兵集群是如何组成的?

  • Sentinel哨兵节点之间是通过Redis的发布者 / 订阅者机制来相互发现的。
  • 在主从集群中,主节点上有一个名为__sentinel__:hello的频道,不同哨兵就是通过它来相互发现,实现互相通信的。
  • 在下图中,哨兵 A 把自己的 IP 地址和端口的信息发布到__sentinel__:hello 频道上,哨兵 B 和 C 订阅了该频道。那么此时,哨兵 B 和 C 就可以从这个频道直接获取哨兵 A 的 IP 地址和端口号。然后,哨兵 B、C 可以和哨兵 A 建立网络连接。
  • 通过这个方式,哨兵 B 和 C 也可以建立网络连接,这样一来,哨兵集群就形成了。

Sentinel怎么知道一个Redis节点是否宕机呢? Sentinel如何判断一个redis实例是否健康?

集群监控原理 - 服务状态监控

Sentinel哨兵节点基于心跳机制监测服务状态,哨兵节点每隔1秒向集群中的每个实例(所有主节点和从节点)发送ping命令,并通过实例的响应结果来做出判断:

  • 主观下线(sdown):如果某Sentinel哨兵节点发现某Redis实例未在规定时间内发送PONG响应,哨兵节点会将该节点标记未主观下线(我个人认为你下线了,但不一定真的下线了,因为是超时未响应,有可能是因为网络阻塞导致的响应超时)。
  • 客观下线(odown):若超过指定数量(quorum)的Sentinel哨兵节点都认为该Redis节点或该实例主观下线,则该实例客观下线  /  如果一个Redis节点被多数哨兵节点都标记为主观下线,那么它将被标记为客观下线。quorum可以在redis.config配置文件里面配,quorum值最好超过Sentinel实例数量的一半,Sentinel节点数量至少3台(3 / 2 => 2)

  • 当主节点被标记为客观下线时, 哨兵节点会触发故障转移过程,它会从所有健康的从节点中选举一个新的主节点,并将所有从节点切换到新的主节点,实现自动故障转移,同时,哨兵节点会更新所有客户端的配置,指向新的主节点。  

集群故障恢复原理 

选举新的Master

一旦发现Master故障,Sentinel需要在Slave中选择一个作为新的Master,选择依据是这样的:

  • 首先判断Slave节点与Master节点断开时间长短,如果断开时间超过(down -after-milliseconds * 10)则会排除该Slave节点,因为断开时间越长,丢失的数据肯定就越多,这样的节点会直接排除,它就不具备选举权
  • 然后判断Slave节点的slave-priority值,默认值都为1,越小优先级越高,如果是0则永不参加选举  =>  既然默认都是1,跳过  
  • 如果slave-priority一样,则判断Slave节点的offset值,offset代表当前Slave节点与Master节点之间数据同步的一个进度,值越大说明跟Master之间的数据越接近,越大说明数据越新,优先级越高
  • 最后是判断Slave节点的run_id  -  运行ID大小,越小优先级越高(通过info  server可以查看run_id)
问题来了,当选出一个新的master后,该如何实现身份切换呢?故障转移步骤有哪些? 
  • 首先要在多个Sentinel中选举一个leader  =>  第一个确认Master客观下线的人会立刻发起投票,一定会成为leader
  • 由leader执行failover故障转移
  • 选定一个Slave作为新的Master,并且执行slaveof  no  one命令,让该节点成为Master
  • 然后让所有其它Slave节点都执行   slaveof  新Master的IP  新Master的Port端口  命令,让这些Slave成为新Master的从节点,开始从新的Master上同步数据
  • 最后,Sentinel修改故障节点的配置,将故障节点标记为Slave,当故障节点恢复后会自动成为新的Master的Slave节点

RedisTemplate的哨兵模式

  • 在Sentinel集群监管下的Redis主从集群,其节点会因为自动故障转移而发送变化,Redis的客户端必须感知这种变化,及时更新连接信息。
  • Spring的RedisTemplate底层利用lettuce实现了节点的感知和自动切换(RedisTemplate底底层就用的lettuce)。 

可以利用RedisTemplate连接哨兵集群:

1. 引入依赖
  • 就是SpringDataRedis的依赖:
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2. 配置哨兵地址

连接哨兵集群与传统单点模式不同,不再需要设置每一个redis的地址,而是直接指定哨兵地址:

spring:
  redis:
    sentinel:
      master: hmaster # 集群名
      nodes: # 哨兵地址列表
        - 192.168.150.101:27001
        - 192.168.150.101:27002
        - 192.168.150.101:27003
3. 配置读写分离

最后,还要配置读写分离,让Java客户端将写请求发送到Master节点,读请求发送到Slave节点。定义一个bean即可:

@Bean
public LettuceClientConfigurationBuilderCustomizer clientConfigurationBuilderCustomizer(){
    return clientConfigurationBuilder -> clientConfigurationBuilder.readFrom(ReadFrom.REPLICA_PREFERRED);
}
总结:
  • Redis哨兵这个集群模式的优点就是为整个集群系统提供了一种故障转移和恢复的能力。

3. Redis分片集群

主从和哨兵模式可以解决高可用、高并发读的问题,但是依然由两个问题没有解决:

  • 海量数据存储问题
  • 高并发写的问题

要解决这两个问题就需要用到Redis分片集群来去解决了。

  • 分片的意思,就是把数据拆分存储到不同节点,这样整个集群的存储数据量就更大了。
  • 基于散列插槽实现集群伸缩以及故障转移

Cluster模式简介

服务端分片技术,3.0版本开始正式提供。

适用场景:

主要是针对海量数据 + 高并发写 + 高可用的场景,如果是海量数据,如果你的数据量很大,那么建议使用Redis Cluster,所有主节点的容量总和就是Redis Cluster可缓存的数据容量。  

Redis Cluster集群节点最小配置6个节点以上(3主3从),其中主节点提供读写操作,从节点作为备用节点,不提供请求,只作为故障转移使用。

  • Redis Cluster是Redis中推荐的分布式集群解决方案,它将数据自动分片到多个节点上,每个节点负责一部分数据。

Redis Cluster采用主从复制模式来提高可用性,每个分片都有一个主节点和多个从节点主节点负责处理写操作,而从节点负责复制主节点的数据并处理读请求。 

Cluster模式的特点是数据分片存储在不同的节点上,每个节点都可以单独对外提供读写服务,不存在单点故障的问题。

分片集群特征: 

  • 集群中有多个Master   =>  解决高并发写的问题,每个Master保存不同分片数据,解决海量数据存储问题   =>  数据存储的上限其实取决于你Master节点的数量
  • 每个Master都可以有多个Slave节点,确保高可用
  • Master之间通过ping监测彼此健康状态,类似哨兵作用  =>  这样就不再需要哨兵机制,却同时具备了哨兵机制的所有功能
  • 客户端请求可以访问任意集群节点,当用户访问集群中的任意节点时,如果该节点无法处理请求,它会将请求自动路由或重定向到正确的节点上,因为将来节点之间会做一种自动的路由或者叫重定向,会把请求路由到一个正确的节点上去访问,所以客户端请求最终都会被转发到数据所在节点,这种重定向机制确保了请求能够被正确的路由到正确的节点,提高了系统的可用性,但请求重定向会重新建立连接,性能上会有一定的损耗

Redis分片集群的结构如图:

1. 搭建分片集群

  • Redis分片集群最少也需要三个Master节点~!
1.1.集群配置

分片集群中的Redis节点必须开启集群模式,一般在配置文件中添加下面参数:

port 7000
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes

其中有3个我们没见过的参数:

  • cluster-enabled是否开启集群模式

  • cluster-config-file集群模式的配置文件名称,无需手动创建,由集群自动维护

  • cluster-node-timeout集群中节点之间心跳超时时间

2. hash slots散列插槽

Redis集群是怎么做到数据分片的? 

  • Redis的数据分片是一种将一个Redis数据集分割成多个部分,分别存储在不同的Redis节点上的技术。
  • 数据要分片存储到不同的Redis节点,肯定需要有分片的依据,这样下次查询的时候才能知道去哪个节点查询。
  • 很多数据分片都会采用一致性hash算法,而Redis则是利用散列插槽或哈希槽(hash slot)的方式实现数据分片。 
  • 在Redis Cluster分片集群中,共有 16384 哈希槽 hash slots,Redis Cluster将整个数据集划分为16384个槽,每个槽都有一个编号(0~16383),每个key通过CRC16算法对key做hash运算得到的结果再对16384取模来决定放置哪个槽,通过这个slot余数值,去找到对应的插槽所对应的节点实例,然后到slots所在的Redis节点执行读写操作,集群的每一个Master节点都会分配一定数量的hash slots,都会负责一部分hash槽或都可以负责多个hash槽。

Redis会根据Key的有效部分计算插槽值,所以hash slot插槽值的计算也分两种情况:

  • 当key中包含"{}"时,且"{}"中至少包含一个字符,"{}"中的部分是有效部分,根据"{}"之间的字符串计算hash slot
  • 当key中不包含"{}"时,整个key都是有效部分则根据整个key字符串计算hash slot 

如何将同一类数据固定的保存在同一个Redis实例?

  • Redis计算key的插槽值时会判断key中是否包含{},如果有则基于{}内的字符计算插槽
  • 数据的key中可以加入{类型},例如key都以{typeId}为前缀,这样同类型数据计算的插槽一定相同 

将16384个插槽分配到不同的实例,具体的分配在集群创建时就已经指定了,查看集群信息时就能看到:

  • 当客户端想要访问某个Key时,它会先根据key计算出对应的槽编号,然后根据槽编号找到负责该槽的节点,然后向该节点发送请求,直接连接到该节点进行操作。
  • 因此,对于客户端而言,Redis的Cluster集群就像是一个大型的、统一的数据库,而不需要关心数据的实际分布情况   =>   数据Key不是与节点绑定,而是与插槽绑定。

Redis Cluster还提供了自动故障转移、集群伸缩等功能,能够比较方便的管理一个大规模的Redis集群。

注意:连接集群时,要加-c参数!

这种哈希槽的数据分片确保了数据存储的均衡和高可用性。

3. 故障转移 

  • 分片集群的节点之间会互相通过ping的方式做心跳检测,超时未回应的节点会被标记为下线状态,当发送Master下线时,会将这个Master的Slave提升为Master。
  • 如果一个节点宕机,集群会自动将故障节点负责的哈希槽重新分配到其余节点上,保证数据的可用性,实现无感知的数据迁移。
  • 利用cluster failover命令可以手动让集群中的某个Master宕机,切换到执行cluster failover命令的这个Slave节点,实现无感知的数据迁移。

4. 集群伸缩

  • 集群伸缩就是动态的增加或移除集群中的节点,以实现集群规模的调整。

面试真题:Redis集群当中怎么加入或删除一个节点?

  • redis-cli  --cluster提供了很多操作集群的命令,查看Redis集群的命令帮助:redis-cli  --cluster help

redis-cli  --cluster:代表集群操作命令 

  • 创建一个集群环境 - create
  • 将一个节点添加到集群里  -  add-node   新增节点的IP:Port    集群中任意一个已经存在的节点的IP:Port
  • 移除一个节点  -  del-node  指定删除节点的IP:端口
  • reshard:重新分片
  • check:检查集群状态

5. RedisTemplate访问分片集群

  • RedisTemplate底层同样基于lettuce实现了分片集群的支持,而使用的步骤与哨兵模式基本一致,只是分片集群的配置方式略有差异。

Redis可以保证数据强一致性吗?

  • Redis集群主从复制采用的是异步复制机制,Redis并不能保证数据的强一致性。

Redis集群会不会出现写丢失?

  • Redis集群不是强一致性会出现丢失写操作数据的情况

比如有以下两个场景:

  1. Redis集群采用的是异步复制,主节点成功即立即但会再同步从节点数据,而由于网络问题从节点可能没有同步到数据
  2. 集群出现了网络分区,可能导致写数据丢失 

Redis是AP还是CP的?

最愚蠢的回答:单机的Redis是CP的,而集群的Redis是AP的。。。。。。。 

  • 首先明确CAP是分布式场景中的理论,如果单机Redis,那就没啥分布式可言了,P都没有了,还谈什么AP、CP呢? 

因此,我认为Redis就是AP的Redis的一致性模式是最终一致性,即在某个时间点读取的数据可能并不是最新的,但最终会达到一致的状态。

Redis没办法保证强一致性的主要原因是,因为它的分布式设计中采用是异步复制,这就导致了节点之间存在数据的同步延迟和不一致的可能性,也就是说,当某个节点上的数据发生改变时,Redis会将这个修改操作发送给其它节点进行同步,但由于网络传输的延迟等原因,这些操作不一定会立即被其它节点接收到并执行,这就可能导致节点之间存在数据不一致的情况。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GaoSSR

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值