Redis(三)主从复制、哨兵、集群

本系列文章:
  Redis(一)数据类型、常用命令
  Redis(二)事务、Redis客户端的使用、持久化
  Redis(三)主从复制、哨兵、集群
  Redis(四)缓存、分布式锁

一、主从复制

  • CAP
      C(Consistent),一致性;A(Availability) ,可用性;P(Partition tolerance ),分区容忍性。
      分布式系统的节点往往都是分布在不同的机器上进行网络隔离开的,这意味着必然会有网络断开的风险,这个网络断开的场景的专业词汇叫网络分区
      在网络分区发生问题时,两个分布式节点之间无法进行通信,我们对一个节点进行的修改操作将无法同步到另外一个节点,所以数据的一致性将无法满足,因为两个分布式节点的数据不再保持一致。除非牺牲可用性,也就是暂停分布式节点服务,在网络分区发生时,不再提供修改数据的功能,直到网络状况完全恢复正常再继续对外提供服务。

      一句话概括CAP原理就是:网络分区发生时,一致性和可用性两难全。
  • 最终一致
      Redis的主从数据是异步同步的,所以分布式的Redis系统并不满足一致性要求。当客户端在Redis的主节点修改了数据后,立即返回,即使在主从网络断开的情况下,主节点依旧可以正常对外提供修改服务,所以Redis满足可用性。
      Redis保证最终一致性,从节点会努力追赶主节点,最终从节点的状态会和主节点的状态将保持一致。如果网络断开了,主从节点的数据将会出现大量不一致,一旦网络恢复,从节点会采用多种策略努力追赶上落后的数据,继续尽力保持和主节点一致。

2.2 主从复制结构

  单机的Redis,能够承载的QPS(一般指每秒查询率)大概就在上万到几万不等。对于缓存来说,一般都是用来支撑读高并发的。因此架构做成主从架构,一主多从较为常见,主负责写,并且将数据复制到其它的slave节点,从节点负责读。这样可以很轻松实现水平扩容,支撑读高并发。

  主数据库可以进行读写操作,当写操作导致数据变化时会自动将数据同步给从数据库。从数据库一般是只读的,并接受主数据库同步过来的数据。一个主数据库可以拥有多个从数据库,而一个从数据库只能拥有一个主数据库。
  Redis的复制结构可以分为三种:一主一从、一主多从、树状主从。

  • 1、一主一从
      一主一从结构是最简单的结构,用于主节点出现宕机时从节点提供故障转移支持
      需要注意的是,如果主节点在没有将数据存到硬盘的情况下重启了,从节点如果继续复制主节点会导致从节点数据也被清空。因此,主节点要重启时,要先在从节点上执行slaveof no one断开与主节点的复制关系,再重启主节点
  • 2、一主多从
      一主多从结构用来实现读写分离,即对于读占比较大的场景,可以把读命令发送到从节点来分担主节点压力。
  • 3、树状主从
      树状主从结构,使得从节点不但可以复制主节点数据,同时可以作为其他从节点的主节点继续向下层复制。当主节点需要挂载多个从节点时为了避免对主节点的性能干扰,可以采用树状主从结构降低主节点压力。

1.2 主从复制作用

  数据冗余: 主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。
  故障恢复: 当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复 (实际上是一种服务的冗余)。
  负载均衡: 在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务 (即写 Redis 数据时应用连接主节点,读 Redis 数据时应用连接从节点),分担服务器负载。尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高 Redis 服务器的并发量。
  高可用基石: 除了上述作用以外,主从复制还是哨兵和集群能够实施的 基础,因此说主从复制是Redis 高可用的基础。

1.3 主从复制原理*

  1. 当启动一个从节点时,它会发送一个PSYNC命令给主节点;
  2. 如果是从节点初次连接到主节点,那么会触发一次全量复制。此时主节点会启动一个后台线程,开始生成一份RDB快照文件;
  3. 同时还会将从客户端client新收到的所有写命令缓存在内存中。 RDB文件生成完毕后, 主节点会将RDB文件发送给从节点,从节点会先将RDB文件写入本地磁盘,然后再从本地磁盘加载到内存中;
  4. 接着主节点会将内存中缓存的写命令发送到从节点,从节点同步这些数据;
  5. 如果从节点跟主节点之间网络出现故障,连接断开了,会自动重连,连接之后主节点仅会将部分缺失的数据同步给从节点。

1.4 主从复制时的问题

1.4.1 读写分离

  即对主节点执行写操作,对从节点执行读操作。当使用从节点响应读请求时,业务端可能会遇到这些问题:数据延迟、读到过期数据。

  • 1、数据延迟
      由于异步复制,延迟不可避免,延迟取决于网络带宽和命令阻塞情况。对于无法容忍大量延迟场景,可以写监控程序监听主从节点的复制偏移量,当延迟较大时触发报警,或通知客户端避免读取延迟过高的从节点。这种方案的一些实现细节:
  1. 监控程序定期检查主从节点的偏移量,它们的差值就是主从节点延迟的字节量。
  2. 当延迟字节量过高时,比如超过10MB。监控程序触发报警并通知客户端从节点延迟过高,可以采用Zookeeper的监听回调机制实现客户端通知。
  3. 客户端接到具体的从节点高延迟通知后,修改读命令路由到其他从节点或主节点上。当延迟恢复后,再次通知客户端,恢复从节点的读命令请求。
  • 2、数据过期
      Redis常常用来缓存数据,对这些缓存数据的管理策略称为删除策略,删除策略主要有两种:惰性删除和定时删除。
      惰性删除:主节点每次处理读取命令时,都会检查键是否超时,如果超时则执行del命令删除键对象,之后del命令也会异步发送给从节点。为了保证复制的一致性,从节点自身永远不会主动删除超时数据。

      定时删除:主节点定时检查键是否过期,过期则执行del命令,之后再同步给从节点。

      如果有大量缓存数据超时,主节点检查数据是否超时的速度跟不上过期速度且主节点没有读取过期键的操作,那么从节点将无法收到del命令。这时在从节点上可以读取到已经超时的数据。

  Redis在3.2版本解决了这个问题,方式是:从节点读取数据之前会检查键的过期时间来决定是否返回数据。

1.4.2 主从配置不一致

  主从节点之间,有些配置是可以不一致的,比如:主节点关闭AOF在从节点开启。但对于内存相关的配置必须要一致,比如maxmemory、hash-max-ziplist-entries等参数。

1.4.3 规避全量复制

  需要全量复制的场景主要有以下3个:第一次建立复制、节点运行ID不匹配、复制积压缓冲区不足。

  • 1、第一次建立复制
      这种情况全量复制无法避免。当对数据量较大且流量较高的主节点添加从节点时,可以在低峰时进行操作,或者尽量规避使用大数据量的Redis节点。
  • 2、节点运行ID不匹配
      如果主从复制关系建立后,主节点因故障重启,那么主节点的运行ID会改变,从节点发现主节点运行ID不匹配时,会认为自己复制的是一个新的主节点从而进行全量复制。
      对于这种情况应该从架构上规避,比如提供故障转移功能。当主节点发生故障后,手动提升从节点为主节点或者采用支持自动故障转移的哨兵或集群方案。
  • 3、复制积压缓冲区不足
      当主从节点网络中断后,从节点再次连上主节点时会发送psync {offset} {runId}命令请求部分复制,如果请求的偏移量不在主节点的积压缓冲区内,则无法提供给从节点数据,因此部分复制会退化为全量复制。
1.4.4 规避复制风暴

  复制风暴指大量从节点对同一主节点或者对同一台机器的多个主节点短时间内发起全量复制的过程。规避方式有如下几个。

  • 1、单主节点复制风暴
      单主节点复制风暴一般发生在主节点挂载多个从节点的场景。当主节点重启恢复后,从节点会发起全量复制流程,这时主节点就会为从节点创建RDB快照。
      如果主节点同时向多个从节点发送RDB快照,可能使主节点的网络带宽消耗严重,造成主节点的延迟变大,极端情况会发生主从节点连接断开,导致复制失败。
      解决方案首先可以减少主节点挂载从节点的数量,或者采用树状复制结构,加入中间层从节点用来保护主节点。示例:
  • 2、单机器复制风暴
      Redis是单线程架构,通常单台机器会部署多个Redis实例。当一台机器上同时部署多个主节点时,如果这台机器出现故障或网络长时间中断,当它重启恢复后,会有大量从节点针对这台机器的主节点进行全量复制,会造成当前机器网络带宽耗尽。
      避免这种情况的方法:

  1、应该把主节点尽量分散在多台机器上,避免在单台机器上部署过多的主节点。
  2、当主节点所在机器故障后提供故障转移机制,避免机器恢复后进行密集的全量复制。

1.5 主从复制的特点

1.5.1 主从复制的核心机制
  • 1、Redis 采用异步方式复制数据到从节点,不过Redis2.8开始,从节点会周期性地确认自己每次复制的数据量。
  • 2、一个主节点是可以配置多个从节点。
  • 3、从节点也可以连接其他的从节点。
  • 4、从节点做复制的时候,不会阻塞主节点的正常工作。
  • 5、从节点在做复制的时候,也不会阻塞对自己的查询操作,它会用旧的数据集来提供服务;但是复制完成的时候,需要删除旧数据集,加载新数据集,这个时候就会暂停对外服务了。
  • 6、从节点主要用来进行横向扩容,做读写分离,扩容的从节点可以提高读的吞吐量。
      
      如果采用了主从架构,那么建议必须开启主节点的持久化,不建议用从节点作为 主节点的数据热备。那样的话,如果关掉主节点的持久化,可能在主节点宕机重启的时候数据是空的,然后可能一经过复制,从节点的数据也丢了。
      同时,主节点的各种备份方案,也需要做。万一本地的所有文件丢失了,从备份中挑选一份 rdb 去恢复主节点数据,这样才能确保启动的时候,主节点是有数据的。即使采用了后续讲解的高可用机制,slave node 可以自动接管 master node,但也可能 sentinel 还没检测到 master failure,master node 就自动重启了,还是可能导致上面所有的 slave node 数据被清空。
1.5.2 主从模式的优缺点*
  • 1、优点
      1)一个主,可以有多个从,并以非阻塞的方式完成数据同步;
      2)从服务器提供读服务,分散主服务的压力,实现读写分离;
      3)从服务器之前可以彼此连接和同步请求,减少主服务同步压力。
  • 2、缺点
      1)不具备容错和恢复功能,主服务存在单点风险;
      2)Redis 的主从复制采用全量复制,需要服务器有足够的空余内存;
      3)主从模式较难支持在线扩容。

1.6 主从复制相关的问题

1.6.1 主从复制的过程
  • 1、slave与master建立连接,发送sync同步命令(slaveof命令)。
  • 2、master会开启一个后台进程,将数据库快照保存到文件中(bgsave),同时master主进程会开始收集新的写命令并缓存到backlog队列。
  • 3、后台进程完成保存后,就将文件发送slave。
  • 4、slave会丢弃所有旧数据,开始载入master发来的快照文件。
  • 5、master向slave发送存储在backlog队列中的写命令,发送完毕后,每执行一个写命令,就向slave发送相同的写命令(异步复制)。
  • 6、slave 执行 master 发来的所有存储在缓冲区的写命令,并从现在开始,接收并执行master传来的每个写命令。

  在接收到master发送的数据初始副本之后,客户端向master写入时,slave都会实时得到更新。在部署好slave之后,客户端就可以向任意一个slave发送读请求了,而不必总是把读请求发送给master。

1.6.2 主从数据库不一致如何解决

  场景描述,对于主从库,读写分离,如果主从库更新同步有时差,就会导致主从库数据的不一致.
  1、忽略这个数据不一致,在数据一致性要求不高的业务下,未必需要时时一致性。
  2、强制读主库,使用一个高可用的主库,数据库读写都在主库,添加一个缓存,提升数据读取的性能。
  3、选择性读主库,添加一个缓存,用来记录必须读主库的数据,将哪个库,哪个表,哪个主键,作为缓存的 key,设置缓存失效的时间为主从库同步的时间,如果缓存当中有这个数据,直接读取主库,如果缓存当中没有这个主键,就到对应的从库中读取。

1.6.3 Redis常见性能问题和解决方案

  1、Master最好不要写内存快照,如果Master写内存快照,save命令调度rdbSave函数,会阻塞主线程的工作,当快照比较大时对性能影响是非常大的,会间断性暂停服务。
  2、如果数据比较重要,某个Slave开启AOF备份数据,策略设置为每秒同步一次。
  3、为了主从复制的速度和连接的稳定性,Master和Slave最好在同一个局域网。
  4、尽量避免在压力很大的主库上增加从库。
  5、主从复制不要用图状结构,用单向链表结构更为稳定,即:Master <- Slave1<- Slave2 <- Slave3…这样的结构方便解决单点故障问题,实现Slave对Master的替换。如果Master挂了,可以立刻启用Slave1做Master,其他不变。

1.6.4 Redis主从复制是如何实现的

  主节点将自己内存中的数据做一份快照,将快照发给从节点,从节点将数据恢复到内存中。之后再每次增加新数据时,主节点以类似于Mysql的二进制日志方式将语句发送给从节点,从节点拿到主节点发送过来的语句进行重放。

1.6.5 Redis常见性能问题和解决方案

  1)Master 最好不要做任何持久化工作,如 RDB 内存快照和 AOF 日志文件。
  2)如果数据比较重要,某个 Slave 开启 AOF 备份数据,策略设置为每秒同步一次。
  3)为了主从复制的速度和连接的稳定性, Master 和 Slave 最好在同一个局域网内。
  4)尽量避免在压力很大的主库上增加从库。
  5)主从复制不要用图状结构,用单向链表结构更为稳定,即: Master <- Slave1 <- Slave2 <-Slave3…这样的结构方便解决单点故障问题,实现 Slave 对 Master的替换。如果 Master 挂了,可以立刻启用 Slave1 做 Master,其他不变。

1.6.6 主从复制的断点续传

  从 Redis2.8 开始,就支持主从复制的断点续传,如果主从复制过程中,网络连接断掉了,那么可以接着上次复制的地方,继续复制下去,而不是从头开始复制一份。
  master node 会在内存中维护一个 backlog,master 和 slave 都会保存一个 replica offset 还有一个 master run id,offset 就是保存在 backlog 中的。如果 master 和 slave 网络连接断掉了,slave会让 master 从上次 replica o?set 开始继续复制,如果没有找到对应的 offset,那么就会执行一次 resynchronization 。

如果根据 host+ip 定位 master node,是不靠谱的,如果 master node 重启或者数据出现了变化,那么 slave node 应该根据不同的 run id 区分。

1.6.7 无磁盘化复制

  master 在内存中直接创建 RDB ,然后发送给 slave,不会在自己本地落地磁盘了。只需要在配置文件中开启 repl-diskless-sync yes 即可。

repl-diskless-sync yes
# 等待 5s 后再开始复制,因为要等更多 slave 重新连接过来
repl-diskless-sync-delay 5
1.6.8 过期 key 处理

  slave 不会过期 key,只会等待 master 过期 key。如果 master 过期了一个 key,或者通过 LRU 淘汰了一个 key,那么会模拟一条 del 命令发送给 slave。

1.6.9 复制的完整流程

  slave node 启动时,会在自己本地保存 master node 的信息,包括 master node 的 host 和ip ,但是复制流程没开始。
  slave node 内部有个定时任务,每秒检查是否有新的 master node 要连接和复制,如果发现,就跟 master node 建立 socket 网络连接。然后 slave node 发送 ping 命令给 master node。如果 master 设置了 requirepass,那么 slave node 必须发送 masterauth 的口令过去进行认证。master node 第一次执行全量复制,将所有数据发给 slave node。而在后续,master node 持续将写命令,异步复制给 slave node。

1.6.10 全量复制

  1、master 执行 bgsave ,在本地生成一份 rdb 快照文件。
  2、master node 将 rdb 快照文件发送给 slave node,如果 rdb 复制时间超过 60秒(repl-timeout),那么 slave node 就会认为复制失败,可以适当调大这个参数(对于千兆网卡的机器,一般每秒传输 100MB,6G 文件,很可能超过 60s)。
  3、master node 在生成 rdb 时,会将所有新的写命令缓存在内存中,在 slave node 保存了 rdb之后,再将新的写命令复制给 slave node。
  4、如果在复制期间,内存缓冲区持续消耗超过 64MB,或者一次性超过 256MB,那么停止复制,复制失败。

client-output-buffer-limit slave 256MB 64MB 60

  5、slave node 接收到 rdb 之后,清空自己的旧数据,然后重新加载 rdb 到自己的内存中,同时基于旧的数据版本对外提供服务。
  6、如果 slave node 开启了 AOF,那么会立即执行 BGREWRITEAOF,重写 AOF。

1.6.11 增量复制

  1、如果全量复制过程中,master-slave 网络连接断掉,那么 slave 重新连接 master 时,会触发增量复制。
  2、master 直接从自己的 backlog 中获取部分丢失的数据,发送给 slave node,默认 backlog 就是 1MB。
  3、master 就是根据 slave 发送的 psync 中的 o?set 来从 backlog 中获取数据的。

1.6.12 心跳

  主从节点互相都会发送 heartbeat 信息。
  master 默认每隔 10秒 发送一次 heartbeat,slave node 每隔 1秒 发送一个 heartbeat。

1.6.13 异步复制

  master 每次接收到写命令之后,先在内部写入数据,然后异步发送给 slave node。

二、哨兵*

  主从复制存在不能自动故障转移、达不到高可用的问题。哨兵模式解决了这些问题。通过哨兵机制可以自动切换主从节点。
  Redis Sentinal着眼于高可用,在master宕机时,会自动将slave提升为master,继续提供服务。

  客户端连接Redis的时候,先连接哨兵,哨兵会告诉客户端Redis主节点的地址,然后客户端连接上Redis并进行后续的操作。当主节点宕机的时候,哨兵监测到主节点宕机,会重新推选出某个表现良好的从节点成为新的主节点,然后通过发布订阅模式通知其他的从服务器,让它们切换主机。

2.1 初识哨兵

2.1.1 基本概念*
名词逻辑结构物理结构
主节点Redis主服务 / 数据库一个独立的Redis进程
从节点Redis从服务 / 数据库一个独立的Redis进程
Redis数据节点主节点和从节点主节点和从节点的进程
Sentinel节点监控Redis数据节点一个独立的Sentinel进程
Sentinel节点集合若干Sentinel节点的抽象合集若干Sentinel节点进程
Redis SentinelRedis高可用实现方案Sentinel节点集合和Redis数据节点进程
应用方一个或多个客户端一个或多个客户端进程或线程

  Redis的主从复制模式可以将主节点的数据改变同步给从节点,这样从节点就可以起到两个作用:

  1. 作为主节点数据的一个备份,保证主节点在出现问题时数据尽量不丢失(主从复制是最终一致性)。
  2. 从节点可以扩展主节点的读能力。

  主从复制也带来了以下问题:

  1. 一旦主节点出现故障,需要手动将一个从节点晋升为主节点,同时需要修改应用方的主节点地址,还需要命令其他从节点去复制新的主节点,整个过程都需要人工干预。(非高可用)
  2. 主节点的写能力受到单机的限制。(分布式问题)
  3. 主节点的存储能力受到单机的限制。(分布式问题)
2.1.2 高可用*

  单纯的主从复制,是非高可用的。Redis Sentinel能够解决这些问题。当主节点出现故障时,Redis Sentinel能自动完成故障发现和故障转移,并通知应用方,从而实现真正的高可用。

  如果想使用Redis Sentinel的话,建议使用2.8以上版本,也就是v2版本的Redis Sentinel。

  Redis Sentinel是一个分布式架构,其中包含若干个Sentinel节点和Redis数据节点,每个Sentinel节点会对数据节点和其余Sentinel节点进行监控,当它发现节点不可达时,会对节点做下线标识。如果被标识的是主节点,它还会和其他Sentinel节点进行“协商”,当大多数Sentinel节点都认为主节点不可达时,它们会选举出一个Sentinel节点来完成自动故障转移的工作,同时会将这个变化实时通知给Redis应用方。

  这里的分布式是指:Redis数据节点、Sentinel节点集合、客户端分布在多个物理节点的架构。

  和Redis主从复制模式相比,Redis Sentinel只是多了若干Sentinel节点,图示:

  以上图中的Redis Sentinel为例,说明一下故障转移的处理逻辑:

  1. 节点出现故障,此时两个从节点与主节点失去连接,主从复制失败。
  2. 每个Sentinel节点通过定期监控发现主节点出现了故障。
  3. 多个Sentinel节点对主节点的故障达成一致,选举出sentinel-3节点作为领导者负责故障转移。
  4. Sentinel领导者节点执行了故障转移,过程:即选出一个从节点,对其执行slaveof no one命令使其成为新的主节点。更新应用方的主节点信息,重新启动应用方。客户端命令另一个从节点去复制新的主节点。待原来的主节点恢复后,让它去复制的主节点。

  故障转移后整个Redis Sentinel的结构:

  从上面的例子,可以看出Redis Sentinel具有以下几个功能:

  1. 集群监控:Sentinel节点会定期检测Redis数据节点、其余Sentinel节点是否正常工作。
  2. 消息通知:如果某个 Redis 实例有故障,那么哨兵负责发送消息作为报警通知给管理员。
  3. 故障转移:实现从节点晋升为主节点并维护后续正确的主从关系(当一个Master不能正常工作时,哨兵(sentinel) 会开始一次自动故障迁移操作,它会将失效Master的其中一个Slave升级为新的Master, 并让失效Master的其他Slave改为复制新的Master; 当客户端试图连接失效的Master时,集群也会向客户端返回新Master的地址,使得集群可以使用Master代替失效Master)。简单来说,如果 master node 挂掉了,会自动转移到 slave node 上。
  4. 配置中心:在Redis Sentinel结构中,客户端在初始化的时候连接的是Sentinel节点集合,从中获取主节点信息。简单来说,如果故障转移发生了,通知 client 客户端新的 master 地址。

  监控和自动故障转移功能,使得哨兵可以及时发现主节点故障并完成转移。而配置提供者和通知功能,则需要在与客户端的交互中才能体现。

  哨兵用于实现 Redis 集群的高可用,本身也是分布式的,作为一个哨兵集群去运行,互相协同工作:

  故障转移时,判断一个 master node 是否宕机了,需要大部分的哨兵都同意才行,涉及到了分布式选举的问题。
  即使部分哨兵节点挂掉了,哨兵集群还是能正常工作的。

  哨兵的核心知识:

  • 哨兵至少需要 3 个实例,来保证自己的健壮性
  • 哨兵 + redis 主从的部署架构,是不保证数据零丢失的,只能保证 redis 集群的高可用性
  • 对于哨兵 + redis 主从这种复杂的部署架构,尽量在测试环境和生产环境,都进行充足的测试和演练。

  Redis Sentinel包含了若个Sentinel节点(Sentinel节点实际上就是独立的Redis节点,只不过它们有一些特殊,它们不存储数据,只支持部分命令),这样做的好处:

  1. 对于节点的故障判断是由多个Sentinel节点共同完成,可以有效地防止误判。
  2. Sentinel节点集合是由若干个Sentinel节点组成,即使个别Sentinel节点不可用,整个Sentinel机制依然正常运行。

  哨兵模式的优点:

  1. 哨兵模式可以主从切换,具备基本的故障转移能力
  2. 哨兵模式具备主从模式的所有优点(由主从模式演变而来)。

  哨兵模式的缺点:

  1. 哨兵模式很难支持在线扩容操作
  2. 集群的配置信息管理比较复杂。

2.2 哨兵实现原理*

  哨兵基本原理是:心跳机制 + 投票裁决
 【哨兵工作原理】

  • 1、每个Sentinel以每秒钟一次的频率向它所知道的Master、Slave以及其他Sentinel实例发送一个PING命令。
  • 2、如果一个实例距离最后一次有效回复PING命令的时间超过指定值(is-master-down-after-milliseconds 指定的毫秒数), 则这个实例会被Sentinel标记为主观下线。
  • 3、如果一个Master被标记为主观下线,则正在监视这个Master的所有Sentinel要以每秒一次的频率确认Master是否真正进入主观下线状态。
  • 4、当有足够数量的Sentinel (大于等于配置文件指定值)在指定的时间范围内确认Master的确进入了主观下线状态, 则Master会被标记为客观下线 。若没有足够数量的Sentinel同意Master已经下线, Master的客观下线状态就会被解除。 若Master重新向Sentinel的PING命令返回有效回复, Master的主观下线状态就会被移除。
  • 5、哨兵节点会选举出哨兵leader,负责故障转移的工作。
  • 6、哨兵leader会推选出某个表现良好的从节点成为新的主节点,然后通知其他从节点更新主节点信息。
2.2.1 三个定时监控任务

  一套合理的监控机制是Sentinel节点判定节点不可达的重要保证,Redis Sentinel通过三个定时监控任务完成对各个节点发现和监控。

  • 1、每隔10秒,每个Sentinel节点会向主节点和从节点发送info命令获取最新的拓扑结构

      这个定时任务的作用具体可以表现在三个方面:
  1. 通过向主节点执行info命令,获取从节点的信息,这也是Sentinel节点不需要显式配置监控从节点的原因。
  2. 当有新的从节点加入时都可以立刻感知出来。
  3. 节点不可达或者故障转移后,可以通过info命令实时更新节点拓扑信息。
  • 2、每隔2秒,每个Sentinel节点会向Redis数据节点的__sentinel__:hello频道上发送该Sentinel节点对于主节点的判断以及当前Sentinel节点的信息,同时每个Sentinel节点也会订阅该频道,来了解其他Sentinel节点以及它们对主节点的判断

      这个定时任务的两个作用:
  1. 发现新的Sentinel节点:通过订阅主节点的__sentinel__:hello了解其他的Sentinel节点信息,如果是新加入的Sentinel节点,将该Sentinel节点信息保存起来,并与该Sentinel节点创建连接。
  2. Sentinel节点之间交换主节点的状态,作为后面客观下线以及领导者选举的依据。
  • 3、每隔1秒,每个Sentinel节点会向主节点、从节点、其余Sentinel节点发送一条ping命令做一次心跳检测,来确认这些节点当前是否可达

      通过前两个定时任务,Sentinel节点实现了和每个节点建立连接,第三个定时任务用来判断Sentinel节点和其他节点之间是否可达。
2.2.2 主观下线和客观下线*

  在上一小节的第三个定时任务中,每个Sentinel节点会每隔1秒对主节点、从节点、其他Sentinel节点发送ping命令做心跳检测,当这些节点超过down-after-milliseconds没有进行有效回复,Sentinel节点就会对该节点做失败判定,这个行为叫做主观下线
  当Sentinel主观下线的节点是主节点时,该Sentinel节点会通过sentinel is-master-down-by-addr命令向其他Sentinel节点询问对主节点的判断,当超过<quorum>个数,就称为客观下线
  在进行客观下线判断时,用到了命令sentinel is-master-down-by-addr,其详细参数:

sentinel is-master-down-by-addr <ip> <port> <current_epoch> <runid>

  ip:主节点IP。
  port:主节点端口。
  current_epoch:当前配置纪元。
  runid:此参数有两种类型:
   1)当runid等于“*”时,作用是Sentinel节点直接交换对主节点下线的判定。
   2)当runid等于当前Sentinel节点的runid时,作用是当前Sentinel节点希望目标Sentinel节点同意自己成为领导者的请求。

  如sentinel-1节点对主节点做主观下线后,会向其余Sentinel节点(假设sentinel-2和sentinel-3节点)发送该命令:

sentinel is-master-down-by-addr 127.0.0.1 6379 0 *

  返回结果包含三个参数,如下所示:

  down_state:目标Sentinel节点对于主节点的下线判断,1是下线,0是在线。
  leader_runid:当leader_runid等于“*”时,代表返回结果是用来做主节点是否不可达,当leader_runid等于具体的runid,代表目标节点同意runid成为领导者。
  leader_epoch:领导者纪元。

2.2.3 选举Leader*

  当Sentinel节点对于主节点已经做了客观下线判断时,接下来Sentinel节点之间会做一个领导者选举的工作,选出一个Sentinel节点作为领导者进行故障转移的工作。Redis使用了Raft算法实现领导者选举。
  Redis Sentinel进行领导者选举的大致思路:

  1. 每个在线的Sentinel节点都有资格成为领导者,当它确认主节点主观下线时候,会向其他Sentinel节点发送sentinel is-master-down-by-addr命令,要求将自己设置为领导者。
  2. 收到命令的Sentinel节点,如果没有同意过其他Sentinel节点的sentinel is-master-down-by-addr命令,将同意该请求,否则拒绝。
  3. 如果该Sentinel节点发现自己的票数已经大于等于max(quorum,num(sentinels)/2+1),那么它将成为领导者。
  4. 如果此过程没有选举出领导者,将进入下一次选举。
2.2.4 故障转移

  领导者选举出的Sentinel节点负责故障转移,具体步骤如下:

  1. 在从节点列表中选出一个节点作为新的主节点,选择方法如下:
     a)过滤:“不健康”(主观下线、断线)、5秒内没有回复过Sentinel节点ping响应、与主节点失联超过down-after-milliseconds*10秒。
     b)选择slave-priority(从节点优先级)最高的从节点列表,如果存在则返回,不存在则继续。
     c)选择复制偏移量最大的从节点(复制的最完整),如果存在则返回,不存在则继续。
     d)选择runid最小的从节点。
  2. Sentinel领导者节点会对第一步选出来的从节点执行slaveof no one命令让其成为主节点。
  3. Sentinel领导者节点会向剩余的从节点发送命令,让它们成为新主节点的从节点,复制规则和parallel-syncs参数有关。
  4. Sentinel节点集合会将原来的主节点更新为从节点,并保持着对其关注,当其恢复后命令它去复制新的主节点。

三、集群*

  哨兵模式解决了主从复制不能自动故障转移、达不到高可用的问题,但还是存在主节点的写能力、容量受限于单机配置的问题。而cluster模式实现了Redis的分布式存储,每个节点存储不同的内容,解决主节点的写能力、容量受限于单机配置的问题。
  Redis cluster集群节点最小配置6个节点以上(3主3从),其中主节点提供读写操作,从节点作为备用节点,不提供请求,只作为故障转移使用。

  Redis Cluster着眼于扩展性,在单个Redis内存不足时,使用Cluster进行分片存储。
  Redis Cluster是Redis在3.0版本推出的分布式解决方案。示例:

上图 展示了 Redis Cluster 典型的架构图,集群中的每一个 Redis 节点都 互相两两相连,客户端任意直连 到集群中的 任意一台,就可以对其他 Redis 节点进行 读写 的操作。

  Redis cluster采用虚拟槽分区,所有的键根据哈希函数映射到0~16383个整数槽内,每个节点负责维护一部分槽以及槽所映射的键值数据。

  • 哈希槽是如何映射到Redis实例上的?
  1. 对键值对的key,使用crc16算法计算一个结果。
  2. 将结果对16384取余,得到的值表示key对应的哈希槽。
  3. 根据该槽信息定位到对应的实例。

  集群内部划分为16384个数据分槽,分布在(本例子中为:三个)主redis中从redis中没有分槽,不会参与集群投票,也不会帮忙加快读取数据,仅仅作为主机的备份。三个主节点中平均分布着16384数据分槽的三分之一,每个主节点之间不会存有有重复数据。 
  Cluster 集群结构特点:

  1. Redis Cluster 所有的物理节点都映射到 [ 0-16383 ] slot 上(不一定均匀分布),Cluster负责维护节点、桶、值之间的关系;
  2. 在 Redis 集群中放置一个 key-value 时,根据 CRC16(key) mod 16384 的值,从之前划分的 16384 个桶中选择一个;
  3. 所有的 Redis 节点彼此互联(PING-PONG 机制),内部使用二进制协议优化传输效率;
  4. 超过半数的节点检测到某个节点失效时则判定该节点失效
  5. 使用端与 Redis 节点链接,不需要中间 proxy 层,直接可以操作,使用端不需要连接集群所有节点,连接集群中任何一个可用节点即可。
  • 集群的主要作用
      1)数据分区。 数据分区 (或称数据分片) 是集群最核心的功能。集群将数据分散到多个节点,一方面突破了 Redis 单机内存大小的限制,存储容量大大增加;另一方面 每个主节点都可以对外提供读服务和写服务,大大提高了集群的响应能力。Redis 单机内存大小受限问题,在介绍持久化和主从复制时都有提及,例如,如果单机内存太大, bgsave 和 bgrewriteaof 的 fork 操作可能导致主进程阻塞,主从环境下主机切换时可能导致从节点长时间无法提供服务,全量复制阶段主节点的复制缓冲区可能溢出……
      2)高可用: 集群支持主从复制和主节点的 自动故障转移 (与哨兵类似),当任一节点发生故障时,集群仍然可以对外提供服务。

【集群的优缺点】

  • 优点
      1、无中心架构,支持动态扩容;
      2、数据按照slot存储分布在多个节点,节点间数据共享,可动态调整数据分布;
      3、高可用性。部分节点不可用时,集群仍可用。集群模式能够实现自动故障转移(failover),节点之间通过gossip协议交换状态信息,用投票机制完成Slave到Master的角色转换。
  • 缺点
      1、不支持批量操作(pipeline)。
      2、数据通过异步复制,不保证数据的强一致性。
      3、事务操作支持有限,只支持多key在同一节点上的事务操作,当多个key分布于不同的节点上时无法使用事务功能。
      4、key作为数据分区的最小粒度,不能将一个很大的键值对象如hash 、 list 等映射到不同的节点。
      5、不支持多数据库空间,单机下的Redis可以支持到16个数据库,集群模式下只能使用1个数据库空间。

3.1 节点通信

  每个集群之间的两两连接是通过CLUSTER MEET <ip> <port>命令发送 MEET 消息完成的。

  • 1、两个端口
      在 哨兵系统 中,节点分为 数据节点 和 哨兵节点:前者存储数据,后者实现额外的控制功能。在 集群中,没有数据节点与非数据节点之分:所有的节点都存储数据,也都参与集群状态的维护。为此,集群中的每个节点,都提供了两个 TCP 端口:
      普通端口: 即我们在前面指定的端口 (7000等)。普通端口主要用于为客户端提供服务 (与单机节点类似);但在节点间数据迁移时也会使用。
      集群端口: 端口号是普通端口 + 10000 (10000是固定值,无法改变),如7000节点的集群端口为17000 。集群端口只用于节点之间的通信,如搭建集群、增减节点、故障转移等操作时节点间的通信;不要使用客户端连接集群接口。为了保证集群可以正常工作,在配置防火墙时,要同时开启普通端口和集群端口。
  • 2、Gossip协议
      gossip 协议包含多种消息,包含 ping , pong , meet , fail 等等。
      节点间通信,按照通信协议可以分为几种类型:单对单、广播、Gossip 协议等。重点是广播和 Gossip的对比。
      广播是指向集群内所有节点发送消息。优点 是集群的收敛速度快(集群收敛是指集群内所有节点获得的集群信息是一致的),缺点 是每条消息都要发送给所有节点,CPU、带宽等消耗较大。
      Gossip 协议的特点是:在节点数量有限的网络中,每个节点都 “随机” 的与部分节点通信 (并不是真正的随机,而是根据特定的规则选择通信的节点),经过一番杂乱无章的通信,每个节点的状态很快会达到一致。Gossip 协议的 优点 有负载 (比广播) 低、去中心化、容错性高 (因为通信有冗余) 等;缺点 主要是集群的收敛速度慢。
  • 3、消息类型
      集群中的节点采用 固定频率(每秒10次) 的 定时任务 进行通信相关的工作:判断是否需要发送消息及消息类型、确定接收节点、发送消息等。如果集群状态发生了变化,如增减节点、槽状态变更,通过节点间的通信,所有节点会很快得知整个集群的状态,使集群收敛。
      节点间发送的消息主要分为 5 种:meet消息 、ping消息 、pong消息 、 fail消息 、 publish消息 。不同的消息类型,通信协议、发送的频率和时机、接收节点的选择等是不同的。
      MEET 消息: 某个节点发送 meet 给新加入的节点,让新节点加入集群中,然后新节点就会开始与其它节点进行通信。;新节点收到 MEET 消息后会回复一个PONG消息。
      PING 消息: 每个节点都会频繁给其它节点发送 ping,其中包含自己的状态还有自己维护的集群元数据,互相通过 ping 交换元数据。
      PONG消息: 返回 ping 和 meeet,包含自己的状态和其它信息,也用于信息广播和更新。
      FAIL 消息: 某个节点判断另一个节点 fail 之后,就发送 fail 给其它节点,通知其它节点说,某个节点宕机啦。
      PUBLISH 消息: 节点收到 PUBLISH 命令后,会先执行该命令,然后向集群广播这一消息,接收节点也会执行该 PUBLISH 命令。

3.2 数据分布(分区算法)*

  分布式数据库首先要解决把整个数据集按照分区规则映射到多个节点的问题,即把数据集划分到多个节点上,每个节点负责整体数据的一个子集。
  常见的分区规则有哈希分区和顺序分区两种。
  顺序分区,比如:1到100个数字,要保存在3个节点上,按照顺序分区,把数据平均分配三个节点上:1号到33号数据保存到节点1上,34号到66号数据保存到节点2上,67号到100号数据保存到节点3上。顺序分区常用在关系型数据库的设计

  哈希分区:例如1到100个数字,对每个数字进行哈希运算,然后对每个数的哈希结果除以节点数进行取余,余数为1则保存在第1个节点上,余数为2则保存在第2个节点上,余数为0则保存在第3个节点,这样可以保证数据被打散,同时保证数据分布的比较均匀。
  Redis Cluster采用的是哈希分区方式

分区方式特点
哈希分区离散度好
数据分布与业务无关
无法顺序访问
顺序分区离散度易倾斜
数据分布与业务有关
可以顺序访问

  常见的哈希分区方式有几种:

3.1.1 节点取余分区*

  使用特定的数据,如Redis的键或用户ID,再根据节点数量N使用公式:hash(key)%N计算出哈希值,用来决定数据映射到哪一个节点上。这种方案存在一个问题:当节点数量变化时,如扩容或收缩节点,数据节点映射关系需要重新计算,会导致数据的重新迁移。
  比如有100个数据,对每个数据进行hash运算之后,与节点数进行取余运算,根据余数不同保存在不同的节点上。
  节点取余方式是非常简单的一种分区方式,但数据迁移时,可能工作量较大。
  节点取余分区的优点:

  1. 客户端分片;
  2. 配置简单:对数据进行哈希,然后取余。

  节点取余分区的缺点:

  1. 数据节点伸缩时,导致数据迁移;
  2. 迁移数量和添加节点数据有关,建议翻倍扩容。

  节点取余分区常用于数据库的分库分表规则,一般采用预分区的方式,提前根据数据量规划好分区数,比如划分为512或1024张表,保证可支撑未来一段时间的数据量,再根据负载情况将表迁移到其他数据库中。扩容时通常采用翻倍扩容,避免数据映射全部被打乱导致全量迁移的情况。示例:

3.1.2 一致性哈希分区*

  一致性 hash 算法将整个 hash 值空间组织成一个虚拟的圆环,整个空间按顺时针方向组织,下一步将各个 master 节点(使用服务器的 ip 或主机名)进行 hash。这样就能确定每个节点在其哈希环上的位置。
  来了一个 key,首先计算 hash 值,并确定此数据在环上的位置,从此位置沿环顺时针“行走”,遇到的第一个 master 节点就是 key 所在位置。
  在一致性哈希算法中,如果一个节点挂了,受影响的数据仅仅是此节点到环空间前一个节点(沿着逆时针方向行走遇到的第一个节点)之间的数据,其它不受影响。增加一个节点也同理。
  然而,一致性哈希算法在节点太少时,容易因为节点分布不均匀而造成缓存热点的问题。为了解决这种热点问题,一致性 hash 算法引入了虚拟节点机制,即对每一个节点计算多个 hash,每个计算结果位置都放置一个虚拟节点。这样就实现了数据的均匀分布,负载均衡。

  相比节点取余,这种方式最大的好处在于加入和删除节点只影响哈希环中相邻的节点,对其他节点无影响。
  一致性哈希分区的缺点:

  1. 加减节点会造成哈希环中部分数据无法命中,需要手动处理或者忽略这部分数据,因此一致性哈希常用于缓存场景。
  2. 当使用少量节点时,节点变化将大范围影响哈希环中数据映射,因此这种方式不适合少量数据节点的分布式方案。
  3. 普通的一致性哈希分区在增减节点时需要增加一倍或减去一半节点才能保证数据和负载的均衡。
3.1.3 虚拟槽分区*

  Redis Cluser采用虚拟槽分区,所有的键根据哈希函数映射到0~16383整数槽内,计算公式:slot=CRC16(key)&16383。每一个节点负责维护一部分槽以及槽所映射的键值数据。

  虚拟槽分区步骤:

  1. 把16384槽按照节点数量进行平均分配,由节点进行管理。
  2. 对每个key按照CRC16(数据通信领域中最常用的一种差错校验码,其特征是信息字段和校验字段的长度可以任意选定)规则进行hash运算。
  3. 把hash结果对16383进行取余。
  4. 把余数发送给Redis节点。
  5. 节点接收到数据,验证是否在自己管理的槽编号的范围。
     1)如果在自己管理的槽编号范围内,则把数据保存到数据槽中,然后返回执行结果;
     2)如果在自己管理的槽编号范围外,则会把数据发送给正确的节点,由正确的节点来把数据保存在对应的槽中;

  从上面可以看出:

  Redis Cluster的节点之间会共享消息,每个节点都会知道是哪个节点负责哪个范围内的数据槽。
  虚拟槽分布方式中,由于每个节点管理一部分数据槽,数据保存到数据槽中。当节点扩容或者缩容时,对数据槽进行重新分配迁移即可,数据不会丢失。

  Redis虚拟槽分区的特点:

  1. 解耦数据和节点之间的关系,简化了节点扩容和收缩难度。
  2. 节点自身维护槽的映射关系,不需要客户端或者代理服务维护槽分区元数据。

  Redis集群相对单机在功能上存在一些限制:

  1. key批量操作支持有限。如mset、mget,目前只支持在同一个槽的key执行批量操作。因为key如果不在一个槽内,就可能存在多个物理节点上。
  2. key事务操作支持有限。同理只支持多key在同一节点上的事务操作,当多个key分布在不同的节点上时无法使用事务功能。
  3. 单机下的Redis可以支持16个数据库,集群模式下只能使用一个数据库空间,即db0
  4. 复制结构只支持一层,从节点只能复制主节点,不支持嵌套树状复制结构。
  • 为什么redis集群的最大槽数是16384个?
      Redis集群有16384个哈希槽,每个key通过CRC16算法计算的结果,对16384取模后放到对应的编号在0-16383之间的哈希槽,集群的每个节点负责一部分哈希槽。
      在redis节点发送心跳包时需要把所有的槽放到这个心跳包里,以便让节点知道当前集群信息,16384=16k,在发送心跳包时使用char进行bitmap压缩后是2k(2 * 8 (8 bit) * 1024(1k) = 2K),也就是说使用2k的空间创建了16k的槽数
      虽然使用CRC16算法最多可以分配65535(2^16-1)个槽位,65535=65k,压缩后就是8k(8 * 8 (8 bit) * 1024(1k) = 8K),也就是说需要需要8k的心跳包,作者认为这样做不太值得;并且一般情况下一个redis集群不会有超过1000个master节点,所以16k的槽位是个比较合适的选择

3.2 集群相关问题

3.2.1 Redis集群会有写操作丢失吗

  Redis并不能保证数据的强一致性,这意味着集群在特定的条件下可能会丢失写操作。
  以下情况可能导致写操作丢失:

  • 过期key被清理;
  • 最大内存不足,导致Redis自动清理部分key以节省空间;
  • 主库故障后自动重启,从库自动同步;
  • 单独的主备方案,网络不稳定触发哨兵的自动切换主从节点,切换期间会有数据丢失。
3.2.2 读写分离怎么实现

  使用读写分离的方式提升数据库的负载能力。将所有的查询处理都放到从服务器上,写处理放在主服务器。

  • 1、使用Spring基于应用层实现

      在进入Service之前,使用AOP来做出判断,是使用写库还是读库,判断依据可以根据方法名判断,比如说以query、find、get等开头的就走读库,其他的走写库。
      具体实现:继承AbstractRoutingDataSource实现动态数据源切换,然后使用ThreadLocal实现简单的读写分离
public class DynamicDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DbContextHolder.getDbType();
    }
}

public class DbContextHolder {
	// 注意:数据源标识保存在线程变量中,避免多线程操作数据源时互相干扰
	private static final ThreadLocal<String> contextHolder=new ThreadLocal<String>();
	public static void setDbType(String dbType){ 
		contextHolder.set(dbType);
	} 
	
	public static String getDbType(){
		String dbType=(String) contextHolder.get(); 
	 	return dbType; 
	}
	
	public static void clearDbType(){
		contextHolder.remove();
	}
}

@Component
@Aspect
public class DataSourceMethodInterceptor {

    @Before("execution(* com.xxx.xxx.xxx.xxx.service.impl.*.*(..))")
    public void dynamicSetDataSoruce(JoinPoint joinPoint) throws Exception {
        String methodName = joinPoint.getSignature().getName();
        // 查询读从库
        if (methodName.startsWith("select") || methodName.startsWith("load") || methodName.startsWith("get") || methodName.startsWith("count") || methodName.startsWith("is")) {
            DynamicDataSourceHolder.setDataSource("slave");
        } else { // 其他读主库
            DynamicDataSourceHolder.setDataSource("master");
        }
    }

}

  优点:

  1. 多数据源切换方便,由程序自动完成;
  2. 不需要引入中间件;
  3. 理论上支持任何数据库;

  缺点:

  1. 由程序员完成,运维参与不到;
  2. 不能做到动态增加数据源;
  • 2、使用中间件实现读写分离
      比如使用阿里的mycat。
      比如要求:

 一主两从,做读写分离。
 多个从库之间实现负载均衡。
 可手动强制部分读请求到主库上。(因为主从同步有延迟,对实时性要求高的系统,可以将部分读请求也走主库)。

  一主两从可以mybatis中配置:

<bean id="master" class="com.alibaba.druid.pool.DruidDataSource" init-method="init"
      destroy-method="close">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="${jdbc.url.master}"></property>
    <property name="username" value="${jdbc.username.master}"></property>
    <property name="password" value="${jdbc.password.master}"></property>
    <property name="maxActive" value="100"/>
    <property name="initialSize" value="10"/>
    <property name="maxWait" value="60000"/>
    <property name="minIdle" value="5"/>
</bean>

<bean id="slave1" class="com.alibaba.druid.pool.DruidDataSource" init-method="init"
      destroy-method="close">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="${jdbc.url.slave1}"></property>
    <property name="username" value="${jdbc.username.slave1}"></property>
    <property name="password" value="${jdbc.password.slave1}"></property>
    <property name="maxActive" value="100"/>
    <property name="initialSize" value="10"/>
    <property name="maxWait" value="60000"/>
    <property name="minIdle" value="5"/>
</bean>

<bean id="slave2" class="com.alibaba.druid.pool.DruidDataSource" init-method="init"
      destroy-method="close">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="${jdbc.url.slave2}"></property>
    <property name="username" value="${jdbc.username.slave2}"></property>
    <property name="password" value="${jdbc.password.slave2}"></property>
    <property name="maxActive" value="100"/>
    <property name="initialSize" value="10"/>
    <property name="maxWait" value="60000"/>
    <property name="minIdle" value="5"/>
</bean>

<bean id="randomStrategy" class="io.shardingjdbc.core.api.algorithm.masterslave.RandomMasterSlaveLoadBalanceAlgorithm" />

<master-slave:data-source id="shardingDataSource" master-data-source-name="master" slave-data-source-names="slave1,slave2" strategy-ref="randomStrategy" />

  使用读写分离,可能会有主从同步延迟的问题,对于一些实时性要求比较高的业务,需强制部分读请求访问主库,可以使用HintManager.setMasterRouteOnly()方法实现

3.2.3 Redis集群方案什么情况下会导致整个集群不可用*

  有A、B、C三个节点的集群,在没有复制模型的情况下,如果节点B失败了,那么整个集群就会以为缺少5501-11000这个范围的槽而不可用。

3.2.4 Redis集群之间是如何复制的

  异步复制。

3.2.5 Redis常见的部署方式有哪些

  Redis的几种常见使用方式包括:单机版、Redis主从、Redis Sentinel(哨兵)、Redis Cluster。
  单机版:很少使用。存在的问题:1、内存容量有限 2、处理能力有限 3、无法高可用。
  主从模式:master节点挂掉后,需要手动指定新的master,可用性不高,基本不用。
  哨兵模式:master节点挂掉后,哨兵进程会主动选举新的master,可用性高,但是每个节点存储的数据是一样的,浪费内存空间。数据量不是很多,集群规模不是很大,需要自动容错容灾的时候使用。
  Redis cluster:主要是针对海量数据+高并发+高可用的场景,如果是海量数据,如果你的数据量很大,那么建议就用Redis cluster,所有主节点的容量总和就是Redis cluster可缓存的数据容量。

3.2.6 Redis cluster 的高可用与主备切换原理

  Redis cluster 的高可用的原理,几乎跟哨兵是类似的。

  • 判断节点宕机
      如果一个节点认为另外一个节点宕机,那么就是 pfail ,主观宕机。如果多个节点都认为另外一个节点宕机了,那么就是 fail ,客观宕机,跟哨兵的原理几乎一样,sdown,odown。
      在 cluster-node-timeout 内,某个节点一直没有返回 pong ,那么就被认为 pfail 。
      如果一个节点认为某个节点 pfail 了,那么会在 gossip ping 消息中, ping 给其他节点,如果超过半数的节点都认为 pfail 了,那么就会变成 fail 。
  • 从节点过滤
      对宕机的 master node,从其所有的 slave node 中,选择一个切换成 master node。
      检查每个 slave node 与 master node 断开连接的时间,如果超过了 cluster-node-timeout * cluster-slave-validity-factor ,那么就没有资格切换成 master 。
  • 从节点选举
      每个从节点,都根据自己对 master 复制数据的 offset,来设置一个选举时间,offset 越大(复制数据越多)的从节点,选举时间越靠前,优先进行选举。
      所有的 master node 开始 slave 选举投票,给要进行选举的 slave 进行投票,如果大部分 master node (N/2 + 1) 都投票给了某个从节点,那么选举通过,那个从节点可以切换成 master。
      从节点执行主备切换,从节点切换为主节点。
  • 与哨兵比较
      整个流程跟哨兵相比,非常类似,所以说,Redis cluster 功能强大,直接集成了 replication 和sentinel 的功能。
  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
Redis 3.2版本支持主从复制哨兵模式。主从复制是指将一个Redis实例作为主节点,其他Redis实例作为从节点,主节点将数据同步到从节点,实现数据的备份和读写分离。哨兵模式是在主从复制的基础上引入了哨兵节点,哨兵节点负责监控主节点的状态,并在主节点宕机时自动将一个从节点升级为新的主节点,确保系统的高可用性。 在Redis 3.2中,启动主从复制哨兵模式的步骤如下: 1. 首先启动主服务器。 2. 然后启动从服务器。 3. 在启动哨兵节点之前,需要修改哨兵配置文件sentinel.conf,配置主节点和从节点的信息。 4. 最后启动哨兵节点。 需要注意的是,启动主服务器和从服务器的顺序很重要,应该先启动主服务器,再启动从服务器。这样可以确保主服务器在启动时已经准备好接收从服务器的连接。 总结起来,启动Redis 3.2的主从复制哨兵模式的步骤是先启动主服务器,再启动从服务器,最后启动哨兵节点。\[1\]\[2\]\[3\] #### 引用[.reference_title] - *1* [Redis主从复制哨兵机制详解](https://blog.csdn.net/weixin_43888891/article/details/131039418)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [Redis主从复制哨兵模式、集群)概述及部署](https://blog.csdn.net/weixin_59663288/article/details/125560643)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值