02-Redis 部署模式和集群(重要)

官网保平安:https://redis.io/

Redis 哈希槽

Redis 集群没有使用一致性 hash,而是引入了哈希槽的概念,当需要在 Redis 集群中放置一个 key-value 时,根据 CRC16(key) mod 16384 的值,决定将一个 key 放到哪个桶中。

Redis 集群最大节点个数是多少

Redis 集群预分好 16384 个桶(哈希槽)。

分区

Redis 是单线程的,如何提高多核 CPU 的利用率?

可以在同一个服务器部署多个 Redis 的实例,并把他们当作不同的服务器来使用,在某些时候,无论如何一个服务器是不够的,所以如果你想使用多个CPU,你可以考虑一下分片(shard)。

为什么要做 Redis 分区

分区可以让 Redis 管理更大的内存,Redis 将可以使用所有机器的内存。如果没有分区,你最多只能使用一台机器的内存。分区使 Redis 的计算能力通过简单地增加计算机得到成倍提升,Redis 的网络带宽也会随着计算机和网卡的增加而成倍增长。

有哪些 Redis 分区实现方案

  • 客户端分区:就是在客户端就已经决定数据会被存储到哪个 Redis 节点或者从哪个 Redis 节点读取。大多数客户端已经实现了客户端分区。

  • 代理分区:意味着客户端将请求发送给代理,然后代理决定去哪个节点写数据或者读数据。代理根据分区规则决定请求哪些 Redis 实例,然后根据 Redis 的响应结果返回给客户端。
    Redis 和 memcached 的一种代理实现就是 Twemproxy。

  • 查询路由(Query routing)的意思是客户端随机地请求任意一个 Redis 实例,然后由 Redis 将请求转发给正确的 Redis 节点。
    Redis Cluster 实现了一种混合形式的查询路由,但并不是直接将请求从一个 Redis 节点转发到另一个 Redis 节点,而是在客户端的帮助下直接 redirected 到正确的 Redis 节点。

Redis 分区有什么缺点

  • 涉及多个 key 的操作通常不会被支持。例如你不能对两个集合求交集,因为他们可能被存储到不同的 Redis 实例(实际上这种情况也有办法,但是不能直接使用交集指令)。
    同时操作多个 key,不能使用 Redis 事务。

  • 分区使用的粒度是 key,不能使用一个非常长的排序 key 存储一个数据集。

  • 当使用分区的时候,数据处理会非常复杂,例如为了备份你必须从不同的 Redis 实例和主机同时收集 RDB/AOF 文件。

  • 分区时动态扩容或缩容可能非常复杂。Redis 集群在运行时增加或者删除 Redis 节点,能做到最大程度对用户透明地数据再平衡,但其他一些客户端分区或者代理分区方法则不支持这种特性。有一种预分片的技术也可以较好的解决这个问题。

分布式 Redis 是前期做还是后期规模上来了再做好?为什么

既然 Redis 是如此的轻量(单实例只使用 1M 内存),为防止以后的扩容,最好的办法就是一开始就启动较多实例。即便你只有一台服务器,你也可以一开始就让 Redis 以分布式的方式运行,使用分区,在同一台服务器上启动多个实例。

一开始就多设置几个 Redis 实例,例如 32 或者 64 个实例,对大多数用户来说这操作起来可能比较麻烦,但是从长久来看做这点牺牲是值得的。

这样的话,当你的数据不断增长,需要更多的 Redis 服务器时,你需要做的就是仅仅将 Redis 实例从一台服务迁移到另外一台服务器而已(而不用考虑重新分区的问题)。一旦你添加了另一台服务器,你需要将你一半的 Redis 实例从第一台机器迁移到第二台机器。

分布式寻址算法

参考:https://www.lmlphp.com/user/6408/article/item/419025/

hash 算法

hashcode % size:hash 算法是对 key 进行 hash 运算后取值,然后对节点的数量取模。接着将 key 存入对应的节点。

但是,一旦其中某个节点宕机,所有的请求过来,都会基于最新的存活的节点数量进行取模运算,这就会导致大多数的请求无法拿到缓存数据。

举个例子,一开始我有 3 个节点,这时大家正常的取模运算将数据基本均匀的存在了 3 个节点上,突然宕机了一台,现在只剩下 2 个有效的节点了,这时所有的运算都会基于最新的 2 个节点进行取模运算,原来的 key 对 2 进行取模运算后,显然和对 3 进行取模运算得到的结果是不一样的。结果是大量的数据请求会直接打在 DB 上,这是不可容忍的。

如果 size 发生变化,比如扩容或者缩容,几乎所有的历史数据都需要重 hash、移动,代价非常大(大量缓存重建)。

一致性 hash 算法

一致性 hash 算法将整个 hash 值空间组织成一个虚拟的圆环(大小为0-2^32-1),整个空间按顺时针方向组织,下一步将各个 master 节点(使用服务器的 ip 或主机名)进行 hash。这样就能确定每个节点在其哈希环上的位置。

来看一个经典的一致性 hash 算法的环状示意图:

来了一个 key,首先计算 hash 值,并确定此数据在环上的位置,从此位置沿环顺时针「行走」,遇到的第一个 master 节点就是 key 所在位置。

在一致性哈希算法中,如果一个节点挂了,受影响的数据仅仅是此节点到环空间前一个节点(沿着逆时针方向行走遇到的第一个节点)之间的数据,其它不受影响。增加一个节点也同理。

一致性哈希算法在节点太少时,容易因为节点分布不均匀而造成缓存热点的问题。为了解决这种热点问题,一致性 hash 算法引入了虚拟节点机制,即对每一个节点计算多个 hash,每个计算结果位置都放置一个虚拟节点。这样就实现了数据的均匀分布,负载均衡。

Python 中是否有相应模块?

模块:hash_ring

Redis Cluster 的 hash slot

Redis Cluster 的实现方案十分的聪明,它的分区方式采用了虚拟槽分区。

Redis Cluster 首先会预设虚拟槽,每个槽就相当于一个数字,有一定范围,每个槽映射一个数据子集。

Redis Cluster 会把 16384 个槽按照节点数量进行平均分配,由节点进行管理。

  • 当一个 key 过来的时候,会对这个 key 按照 CRC16 规则进行 hash 运算。
  • 把 hash 结果对 16383 进行取余。
  • 把余数发送给 Redis 节点。
  • 节点接收到数据,验证是否在自己管理的槽编号的范围
    • 如果在自己管理的槽编号范围内,则把数据保存到数据槽中,然后返回执行结果。
    • 如果在自己管理的槽编号范围外,则会把数据发送给正确的节点,由正确的节点来把数据保存在对应的槽中。

虚拟槽分布方式中,由于每个节点管理一部分数据槽,数据保存到数据槽中。当节点扩容或者缩容时,对数据槽进行重新分配迁移即可,数据不会丢失。

部署方式

Redis 集群就是分区的一种的实现。

单机版

特点:简单。

问题:1、内存容量有限。2、处理能力有限。3、无法高可用。

主从复制

Redis 的复制功能允许用户根据一个 Redis 服务器来创建任意多个该服务器的复制品,其中被复制的服务器为主服务器(master),而通过复制创建出来的服务器复制品则为从服务器(slave)。只要主从服务器之间的网络连接正常,主从服务器两者会具有相同的数据,主服务器就会一直将发生在自己身上的数据更新同步给从服务器,从而一直保证主从服务器的数据相同。

特点:

  • master/slave 数据相同
  • 降低 master 读压力,转交从库

问题:

  • 无法保证高可用。
  • 没有解决 master 写的压力。

注意:

  • 如果采用主从架构,那么必须开启 master node 的持久化,不建议用 slave node 作为 master node 的数据热备,因为那样的话,如果你关掉 master 的持久化, master 突然宕机,然后重启时数据是空的,然后经过复制,slave node 的数据也丢了。

  • 另外,master 的各种备份方案也需要做。万一本地的所有文件丢失了,从备份中挑选一份 rdb 去恢复 master,这样才能确保启动的时候,是有数据的。

  • slave node 可以自动接管 master node,但也可能 sentinel 还没检测到 master failure,master node 就自动重启了,还是可能导致上面所有的 slave node 数据被清空。

Redis 如何实现主从复制?以及数据同步机制?

一台服务器,启动两个 Redis 服务,分别创建 6379 和 6380 配置文件

  • redis.conf:6379 为默认配置文件,作为 Master 服务配置。
  • redis_6380.conf:6380 为同步配置,作为 Slave 服务配置。

配置 slaveof 同步指令

  • 在 Slave 对应的 conf 配置文件中,添加以下内容:slaveof 127.0.0.1 6379

数据同步步骤:

  1. 当 从Redis 和 主Redis 建立主从关系后,会向主数据库发送 SYNC 命令。
  2. 主Redis 接收到 SYNC 命令后会开始在后台保存快照(RDB持久化过程),并将期间接收到的写命令缓存起来。
  3. 当快照完成后,主Redis 会将快照文件和所有缓存的写命令发送给 从Redis。
  4. 从Redis 接收到后,会载入快照文件并且执行收到的缓存的命令。
  5. 之后,主Redis 每当接收到写命令时就会将命令发送 从Redis,从而保证数据的一致。

主从复制的原理

1、主从架构的核心原理

当启动一个 slave 的时候,它会发送一个 PSYNC 命令给 master

  • 如果这是 slave 重新连接 master,那么 master 仅仅会复制 slave 缺少的数据,
  • 如果这是 slave 第一次连接 master,那么会触发一次 full resynchronization

开始 full resynchronization 的时候,master 会启动一个后台线程,开始生成一份 RDB 快照文件,同时还会将从客户端收到的所有写命令缓存在内存中。RDB 文件生成完毕之后,master 会将这个 RDB 发送给 slave,slave 会先写入本地磁盘,然后再从本地磁盘加载到内存中。然后 master 会将内存中缓存的写命令发送给 slave,slave 也会同步这些数据。

slave 如果跟 master 有网络故障,断开了连接,会自动重连。master 如果发现有多个 slave 都来重新连接,仅仅会启动一个 rdb save 操作,用一份数据服务所有 slave。

2、主从复制的断点续传

从 Redis 2.8 开始,就支持主从复制的断点续传,如果主从复制过程中,网络连接断掉了,那么可以接着上次复制的地方,继续复制下去,而不是从头开始复制一份

master 会在内存中常见一个 backlog,master 和 slave 都会保存一个 replica offset 还有一个 master id,offset 就是保存在 backlog 中的。如果 master 和 slave 网络连接断掉了,slave 会让 master 从上次的 replica offset 开始继续复制

但是如果没有找到对应的 offset,那么就会执行一次 resynchronization

3、无磁盘化复制

master 在内存中直接创建 rdb,然后发送给 slave,不会在自己本地落地磁盘了。

repl-diskless-sync repl-diskless-sync-delay,等待一定时长再开始复制,因为要等更多 slave 重新连接过来

4、过期 key 处理

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

由于主从延迟导致读取到过期数据怎么处理?

  1. 通过 scan 命令扫库:当 Redis 中的 key 被 scan 的时候,相当于访问了该 key,同样也会做过期检测,充分发挥 Redis 惰性删除的策略。这个方法能大大降低了脏数据读取的概率,但缺点也比较明显,会造成一定的数据库压力,否则影响线上业务的效率。
  2. Redis 加入了一个新特性来解决主从不一致导致读取到过期数据问题,增加了 key 是否过期以及对主从库的判断,如果 key 已过期,当前访问的 master 则返回 null;当前访问的是从库,且执行的是只读命令也返回 null。

Redis 主从架构数据会丢失吗,为什么?

  • Sentinel 可以防止脑裂吗?

有两种数据丢失的情况:

  1. 异步复制导致的数据丢失:因为 master -> slave 的复制是异步的,所以可能有部分数据还没复制到 slave,master 就宕机了,此时这些部分数据就丢失了。
  2. 脑裂导致的数据丢失:
    1. 某个 master 所在机器突然脱离了正常的网络,跟其他 slave 机器不能连接,但是实际上 master 还运行着,此时哨兵可能就会认为 master 宕机了,然后开启选举,将其他 slave 切换成了 master。
    2. 这个时候,集群里就会有两个master,也就是所谓的脑裂。此时虽然某个 slave 被切换成了 master,但是可能 client 还没来得及切换到新的 master,还继续写向旧 master 的数据可能也丢失了。
    3. 因此旧 master 再次恢复的时候,会被作为一个 slave 挂到新的 master上去,自己的数据会清空,重新从新的 master 复制数据。

如何解决主从架构数据丢失的问题?

数据丢失的问题是不可避免的,但是我们可以尽量减少。

在 Redis 的配置文件里设置参数

1
2
min-slaves-to-write 1   # 默认 0
min-slaves-max-lag 10 # 默认 10

上面的配置的意思是:要求至少有 1 个 slave,数据复制和同步的延迟不能超过 10 秒。如果说一旦所有的 slave,数据复制和同步的延迟都超过了 10 秒钟,那么这个时候,master 就不会再接收任何请求了。

减小 min-slaves-max-lag 参数的值,这样就可以避免在发生故障时大量的数据丢失。

那么对于 client,我们可以采取降级措施,将数据暂时写入本地缓存和磁盘中,在一段时间后重新写入 master 来保证数据不丢失;也可以将数据写入 Kafka 消息队列,隔一段时间去消费 Kafka 中的数据。

sentinel 哨兵(>=2.8版本)

Redis sentinel 是一个分布式系统,Redis sentinel 监控 Redis 主从服务器,并在主服务器下线时自动进行故障转移。其中三个特性:

  • 监控(Monitoring):Sentinel 会不断地检查你的主服务器和从服务器是否运作正常。
  • 提醒(Notification):当被监控的某个 Redis 服务器出现问题时,Sentinel 可以通过 API 向管理员或者其他应用程序发送通知。
  • 自动故障迁移(Automatic failover):当一个主服务器不能正常工作时,Sentinel 会开始一次自动故障迁移操作。

特点:

  • 监控各个节点
  • 保证高可用
  • 自动故障迁移

缺点:

  • 主从模式,切换需要时间,可能会丢数据。
  • 没有解决 master 写的压力。

原理:

  1. 三个定时监控任务:

    1. 每隔10s,每个S节点(哨兵节点)会向主节点和从节点发送 info 命令获取最新的拓扑结构。
    2. 每隔 2s,每个S节点(哨兵节点)会向某频道上发送该S节点(哨兵节点)对于主节点的判断以及当前Sl节点的信息,同时每个S节点(哨兵节点)也会订阅该频道,来了解其他S节点(哨兵节点)以及它们对主节点的判断(做客观下线依据)。
    3. 每隔 1s,每个S节点(哨兵节点)会向主节点、从节点、其余S节点(哨兵节点)发送一条 ping 命令做一次心跳检测(心跳检测机制),来确认这些节点当前是否可达。
  2. 主客观下线:

    1. 主观下线(sdown):根据第三个定时任务对没有有效回复的节点做主观下线处理。
    2. 客观下线(odown):若主观下线的是主节点,会咨询其他 S节点 对该主节点的判断,超过半数,对该主节点做客观下线。
  3. 选举出某一哨兵节点作为领导者,来进行故障转移。
    选举方式:raft 算法。每个S节点(哨兵节点)有一票同意权,哪个S节点(哨兵节点)做出主观下线的时候,就会询问其他S节点(哨兵节点)是否同意其为领导者。获得半数选票的则成为领导者。基本谁先做出客观下线,谁成为领导者。

  4. 故障转移(选举新主节点流程):

    如果一个 master 被认为 odown 了,而且 majority(大多数) 哨兵都允许了主备切换,那么某个哨兵就会执行主备切换操作,此时首先要选举一个 slave,会考虑 slave 的一些信息。

    • 跟 master 断开连接的时长。如果一个 slave 跟 master 断开连接已经超过了 down-after-milliseconds 的 10 倍,外加master 宕机的时长,那么该 slave 就被认为不适合选举为 master。
    1
    ( down-after-milliseconds * 10) + milliseconds_since_master_is_in_SDOWN_state
    • slave 优先级。按照 slave 优先级进行排序,slave priority 越低,优先级就越高。

    • 复制 offset。如果 slave priority 相同,那么看 replica offset,哪个 slave 复制了越多的数据,offset 越靠后,优先级就越高。

    • run id。如果上面两个条件都相同,那么选择一个 run id 比较小的 slave。

sentinel 的作用

  1. 帮助我们自动在主从之间进行切换。
  2. 检测主从架构中主服务器是否挂掉,且超过一半的 sentinel 检测到挂了之后才进行进行切换。
  3. 如果主修复好了,再次启动时候,会变成从。

sentinel 设置

  1. 启动主 Redis:redis-server /etc/redis-6379.conf

  2. 启动从 Redis:redis-server /etc/redis-6380.conf

  3. 找到 /etc/redis-sentinel-8001.conf 配置文件,修改:

    • port = 8001:哨兵的端口。
    • sentinel monitor mymaster 127.0.0.1 6379 2
      • mymaster 是哨兵所在分组的名称。
      • 后面的 IP+端口 是 master 的IP和端口。
      • 最后的 2,意思是 master 被认为的宕机,需要选举新的 master 时,至少要争取到 2 票的赞同,否则故障不会自动转移。
  4. 找到 /etc/redis-sentinel-8002.conf 配置文件,修改:

    • port = 8002:哨兵的端口。
    • sentinel monitor mymaster 127.0.0.1 6379 2
  5. 启动两个哨兵:

    1. 方式一:redis-server --sentinel 命令启动哨兵,示例:redis-server /path/to/sentinel.conf --sentinel
    2. 方式二:redis-sentinel 命令启动哨兵,示例:redis-sentinel /path/to/sentinel.conf

    两个命令都可以启动哨兵服务。

为什么 Redis 哨兵集群只有 2 个节点无法正常工作?

哨兵集群必须部署 2 个以上节点。

如果两个哨兵实例,即两个 Redis 实例,一主一从的模式。

则 Redis 的配置 quorum = 1,表示一个哨兵认为 master 宕机即可认为 master 已宕机。

但是如果是 机器1 宕机了,那 哨兵1 和 master 都宕机了,虽然 哨兵2 知道 master 宕机了,但是这个时候,需要 majority,也就是大多数哨兵都是运行的,2 个哨兵的 majority 就是 2(2 的 majority=2,3 的 majority=2,5 的 majority=3,4 的 majority=2),2 个哨兵都运行着,就可以允许执行故障转移。

但此时 哨兵1 没了就只有 1 个哨兵了了,此时就没有 majority 来允许执行故障转移,所以故障转移不会执行。

集群(proxy 型)

Redis 集群常用的代理有 Twemproxy 和 Codis

Twemproxy

Twemproxy 是一个 Twitter 开源的一个 Redis 和 Memcache 快速/轻量级代理服务器;Twemproxy 是一个快速的单线程代理程序,支持 Memcached ASCII 协议和 Redis 协议。

特点:

  • 多种 hash 算法:MD5、CRC16、CRC32、CRC32a、hsieh、murmur、Jenkins。
  • 支持失败节点自动删除。
  • 后端 Sharding 分片逻辑对业务透明,业务方的读写方式和操作单个 Redis 一致。

缺点:

  • 增加了新的 proxy,需要维护其高可用。
  • failover 逻辑需要自己实现,其本身不能支持故障的自动转移可扩展性差,进行扩缩容都需要手动干预。

集群(直连型)

Redis 的哨兵模式基本已经可以实现高可用,读写分离,但是在这种模式下每台 Redis 服务器都存储相同的数据,很浪费内存,所以在 Redis3.0 上加入了 Cluster 集群模式,实现了 Redis 的分布式存储,对数据进行分片,也就是说每台 Redis 节点上存储不同的内容。

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

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

Redis-Cluster 采用无中心结构,每个节点保存数据和整个集群状态,每个节点都和其他所有节点连接。

特点:

  • 无中心架构(不存在哪个节点影响性能瓶颈),没有 proxy 层。
  • 数据按照 slot 存储分布在多个节点,节点间数据共享,可动态调整数据分布。
  • 可扩展性,可线性扩展到 1000 个节点,节点可动态添加或删除。
  • 高可用性,部分节点不可用时,集群仍可用。通过增加 Slave 做备份数据副本。
  • 实现故障自动 failover,节点之间通过 gossip 协议交换状态信息,用投票机制完成 Slave 到 Master 的角色提升。

缺点:

  • Client 实现复杂,驱动要求实现 Smart Client,缓存 slots mapping 信息并及时更新,提高了开发难度,客户端的不成熟影响业务的稳定性。目前仅 JedisCluster 相对成熟,异常处理部分还不完善,比如常见的“max redirect exception”。
  • 节点会因为某些原因发生阻塞(阻塞时间大于 clutser-node-timeout),被判断下线,这种 failover 是没有必要的。
  • 数据通过异步复制,不保证数据的强一致性。
  • 多个业务使用同一套集群时,无法根据统计区分冷热数据,资源隔离性较差,容易出现相互影响的情况。
  • Slave 在集群中充当“冷备”,不能缓解读压力,当然可以通过 SDK 的合理设计来提高 Slave 资源的利用率。
  • Key 批量操作限制,如使用 mset、mget 目前只支持具有相同 slot 值的 Key 执行批量操作。对于映射为不同 slot 值的 Key 由于 Keys 不支持跨 slot 查询,所以执行 mset、mget、sunion 等操作支持不友好。
  • Key 事务操作支持有限,只支持多 key 在同一节点上的事务操作,当多个 Key 分布于不同的节点上时无法使用事务功能。
  • Key 作为数据分区的最小粒度,不能将一个很大的键值对象如 hash、list 等映射到不同的节点。
  • 不支持多数据库空间,单机下的 Redis 可以支持到 16 个数据库,集群模式下只能使用1个数据库空间,即db 0。
  • 复制结构只支持一层,从节点只能复制主节点,不支持嵌套树状复制结构。
  • 避免产生 hot-key,导致主库节点成为系统的短板。
  • 避免产生 big-key,导致网卡撑爆、慢查询等。
  • 重试时间应该大于 cluster-node-time 时间。
  • Redis Cluster 不建议使用 pipeline 和 multi-keys 操作,减少 max redirect 产生的场景。

方案说明

  1. 通过哈希的方式,将数据分片,每个节点均分存储一定哈希槽区间的数据,默认分配了 16384 个槽位。
  2. 每份数据分片会存储在多个互为主从的多节点上。
  3. 数据写入先写主节点,再同步到从节点(支持配置为阻塞同步)。
  4. 同一分片多个节点间的数据不保持一致性。
  5. 读取数据时,当客户端操作的key没有分配在该节点上时,Redis 会返回转向指令,指向正确的节点。
  6. 扩容时需要把旧节点的数据迁移一部分到新节点。

在 Redis cluster 架构下,每个 Redis 要放开两个端口号,比如一个是 6379,另外一个就是加 1w 的端口号,比如 16379。

16379 端口号是用来进行节点间通信的,也就是 cluster bus 的东西,cluster bus 的通信用来进行故障检测、配置更新、故障转移授权。

cluster bus 用了另外一种二进制的协议,gossip 协议,用于节点间进行高效的数据交换,占用更少的网络带宽和处理时间。

如何实现 Redis 集群

Redis 集群是基于【分片】来完成。集群是将你的数据拆分到多个 Redis 实例的过程,可以使用很多电脑的内存总和来支持更大的数据库。如果没有分片,你就只能使用单机能支持的内存容量。

Redis 将所有能放置数据的地方创建了 16384 个哈希槽。如果设置集群的话,可以为每个实例分配哈希槽:

  • 192.168.1.20【0-5000】
  • 192.168.1.21【5001-10000】
  • 192.168.1.22【10001-16384】

以后如果在 Redis 中写值如 set k1 123, Redis 会将 k1 通过 crc16 的算法转换成一个数字,然后再将该数字和 16384 求余,如果得到余数 3000,那么就将该值写入到 192.168.1.20 实例中。

Redis cluster 节点间通信是什么机制?

Redis cluster 节点间采取 gossip 协议进行通信,所有节点都持有一份元数据,不同的节点如果出现了元数据的变更之后不断地将元数据发送给其他节点让其他节点进行数据变更。

节点互相之间不断通信,保持整个集群所有节点的数据是完整的。
主要交换故障信息、节点的增加和移除、hash slot 信息等。

这种机制的好处在于,元数据的更新比较分散,不是集中在一个地方,更新请求会陆陆续续,打到所有节点上去更新,有一定的延时,降低了压力;

缺点,元数据更新有延时,可能导致集群的一些操作会有一些滞后。

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

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

判断节点宕机

如果一个节点认为另外一个节点宕机,那么就是 pfail(主观宕机)。如果多个节点都认为另外一个节点宕机了,那么就是 fail(客观宕机),跟哨兵的原理几乎一样,sdown,odown。

cluster-node-timeout 内,某个节点一直没有返回 pong ,那么就被认为 pfail

如果一个节点认为某个节点 pfail 了,那么会在 gossip ping 消息中, ping 给其他节点,如果超过半数的节点都认为 pfail 了,那么就会变成 fail

从节点过滤

对宕机的 master,从其所有的 slave 中,选择一个切换成 master。

检查每个 slave 与 master 断开连接的时间,如果超过了 cluster-node-timeout * cluster-slave-validity-factor ,那么就没有资格切换成 master

从节点选举

每个从节点,都根据自己对 master 复制数据的 offset,来设置一个选举时间,offset 越大(复制数据越多)的从节点,选举时间越靠前,优先进行选举。

所有的 master 开始 slave 选举投票,给要进行选举的 slave 进行投票,如果大部分 master (N/2 + 1) 都投票给了某个从节点,那么选举通过,那个从节点可以切换成 master。

从节点执行主备切换,从节点切换为主节点。

与哨兵比较

整个流程跟哨兵相比,非常类似,所以说,Redis cluster 功能强大,直接集成了 replication 和 sentinel 的功能。

Redis Cluster 扩容缩容期间可以提供服务吗

在 Redis Cluster 进行扩容或缩容的过程中,服务可以不间断提供。Redis Cluster 采用无中心架构,其中每个节点都知道集群的所有节点和数据分布情况。当新节点加入或现有节点被移除时,集群会自动地重新分配数据。

扩容时,你可以将新节点添加到集群,并且当数据迁移完成后,新节点将开始处理客户端的请求。

缩容时,你可以将现有节点标记为不再使用,并将其数据迁移到其他节点。当完成迁移并确认节点可以安全地移除后,你可以将其从集群中移除。

以下是一个简单的 Redis Cluster 扩容的例子(假设你已经有一个运行的 Redis Cluster):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 假设你已经有一个运行的 Redis Cluster,并且你想要添加一个新节点

# 在新节点上安装并运行Redis
wget http://download.redis.io/releases/redis-6.2.6.tar.gz
tar xzf redis-6.2.6.tar.gz
cd redis-6.2.6
make
src/redis-server --cluster-enabled yes --port 6380 --cluster-config-file nodes-6380.conf --cluster-node-timeout 5000 --appendonly yes --appendfilename appendonly-6380.aof --dbfilename dump-6380.rdb --logfile 6380.log

# 将新节点添加到集群
redis-cli --cluster add-node 127.0.0.1:6380 127.0.0.1:6379

# 重新分配 slots
redis-cli --cluster reshard 127.0.0.1:6380

# 将数据迁移到新节点
redis-cli --cluster rebalance 127.0.0.1:6380

缩容时,你需要先将要移除的节点数据迁移到其他节点,然后再将其移除。这个过程可以通过 redis-cli –cluster reshard 和 redis-cli –cluster forget 命令来完成。

请注意,实际的 Redis Cluster 扩容和缩容操作可能涉及到数据迁移和重分配,这些操作会耗费一定的时间,在此期间,集群仍然能够提供服务,但可能会影响性能。在执行这类操作前,请确保已经做好了充分的备份和测试。

集群方案比较

  1. Twemproxy:Twemproxy 是 Twtter 开源的一个 Redis 和 Memcache 代理服务器,主要用于管理 Redis 和 Memcached 集群,减少与 Cache 服务器直接连接的数量。他的后端是多台 Redis 或 memcached 所以也可以被称为分布式中间件。
    使用时在本需要连接 Redis 的地方改为连接 twemproxy,它会以一个代理的身份接收请求并使用一致性 hash 算法,将请求转接到具体 Redis,将结果再返回 Twemproxy。
    作用:

    • 通过代理的方式减少缓存服务器的连接数。
    • 自动在多台缓存服务器间共享数据。
    • 通过配置的方式禁用失败的结点。
    • 运行在多个实例上,客户端可以连接到首个可用的代理服务器。
    • 支持请求的流式与批处理,因而能够降低来回的消耗。
      缺点:
    • Twemproxy 自身单端口实例的压力;
    • 使用一致性 hash 后,Redis 节点数量改变的时候相应的计算值也会改变,数据无法自动移动到新的节点。
  2. Codis:豌豆荚技术团队研发的一个分布式 Redis 解决方案。
    对于上层的应用来说,连接到 Codis-Proxy(Redis代理服务)和连接原生的 Redis-Server 没有明显的区别, 上层应用可以像使用单机的 Redis 一样使用, Codis 底层会处理请求的转发, 不停机的数据迁移等工作,所有后边的一切事情, 对于前面的客户端来说是透明的, 可以简单的认为后边连接的是一个内存无限大的 Redis 服务。
    基本和 twemproxy 一致的效果,但它支持在节点数量改变情况下,旧节点数据可恢复到新 hash 节点。

  3. Redis cluster3.0:官方提供的集群方案,特点在于他的分布式算法不是一致性 hash,而是 hash 槽的概念,以及自身支持节点设置从节点。


02-Redis 部署模式和集群(重要)
https://flepeng.github.io/interview-41-数据库-41-Redis-02-Redis-部署模式和集群(重要)/
作者
Lepeng
发布于
2020年8月8日
许可协议