Redis的集群模式(sentinel和cluster)

一、主从复制

官网:Replication

Redis的主从复制默认是异步的(异步确认),这就保证了Redis的低延迟和高性能。客户端可以使用wait命令来同步的复制某些数据。

Redis主从复制的一些重要特点:

1.主从复制是异步的,指的是slave会进行异步确认

2.复制在master端是非阻塞的,指的是master在一个或多个slave初次同步或者部分重同步时,可以继续处理查询请求。

在生产环境中,一定要开启master上的持久化机制,而不允许关闭master的持久化,仅用slave做热备。因为master一旦宕机,重启后由于内存中数据为空,主从同步会将slave也清空,这完全就是灾难。

1.主从复制的工作原理

每一个 Redis master 都有一个 replication ID :这是一个较大的伪随机字符串,标记了一个给定的数据集。每个 master 也持有一个偏移量,master 将自己产生的复制流发送给 slave 时,发送多少个字节的数据,自身的偏移量就会增加多少,目的是当有新的操作修改自己的数据集时,它可以以此更新 slave 的状态。复制偏移量即使在没有一个 slave 连接到 master 时,也会自增,所以基本上每一对给定的{Replication ID, offset}都会标识一个 master 数据集的确切版本。

当 slave 连接到 master 时,它们使用 PSYNC 命令来发送它们记录的旧的 master replication ID 和它们至今为止处理的偏移量。通过这种方式, master 能够仅发送 slave 所需的增量部分。(可以成为断点续传)

但是如果 master 的缓冲区中没有足够的命令积压缓冲记录,或者如果 slave 引用了不再知道的历史记录(replication ID),则会转而进行一个全量重同步:在这种情况下, slave 会得到一个完整的数据集副本,从头开始。

全量重同步

下面是一个全量同步的工作细节:

master开启一个后台保存进程,以便于生产一个 RDB 文件。同时它开始缓冲所有从客户端接收到的新的写入命令。当后台保存完成时,master 将数据集文件传输给slave,slave将之保存在磁盘上,然后加载文件到内存。再然后 master 会发送所有缓冲的命令发给 slave。

2.无磁盘复制

上面提到了全量重同步时,master会先在磁盘上生成一个RDB文件,如果磁盘性能很低的话,这对master是一个压力很大的操作。Redis 2.8.18开始支持无磁盘复制,即子进程直接发送 RDB文件数据给 slave,无需使用磁盘作为中间储存介质。需要配置两个参数:

  • repl-diskless-sync :默认为no,设为yes开启。
  • repl-diskless-sync delay:等待一定时长才开始复制,目的是为了更多的slave连接就绪,同时进行。

3.复制时如何处理key的过期

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

二、sentinel哨兵集群

官网:Redis Sentinel

哨兵集群是建立在主从复制基础之上的。哨兵的主要作用如下:

  • 监控(Monitoring): Sentinel 会检查主服务器和从服务器是否运作正常。
  • 提醒(Notification): 当被监控的某个 Redis 服务器出现问题时, Sentinel 可以通过 API 向管理员或者其他应用程序发送通知。
  • 自动故障迁移(Automatic failover): 当一个主服务器不能正常工作时, Sentinel 会进行自动故障迁移, 将失效主服务器的其中一个从服务器提升为新的主服务器, 并让失效的主服务器的其他从服务器改为复制新的主服务器; 当客户端试图连接失效的主服务器时, 集群也会向客户端返回新主服务器的地址, 使得集群可以使用新主服务器代替失效服务器。

为了高可用性,哨兵也可以有多个。

配置哨兵

配置哨兵集群时,需要注意sentinel.conf配置文件中的几个参数:

  • quorum:判定主节点最终不可达所需要的票数。

①参数用于故障发现和判定,一般建议将其设置为:sentinels数量/2+1

②和领导者选举的判定。至少要有max(quorum,sentinels数量/2+1)个Sentinel节点参与选举,才能选出领导者,从而完成故障转移

  • majority

为什么redis哨兵集群必须至少有3个节点,只有2个节点无法正常工作?

2个哨兵的majority就是2(2的majority=2,3的majority=2,4的majority=2,5的majority=3)

数据一致性问题(数据丢失问题)

1、两种数据丢失的情况

主备切换的过程,可能会导致数据丢失

(1) 异步复制导致的数据丢失

因为主从复制是异步的,所以可能有部分数据还没复制到slave,master就宕机了,此时这些部分数据就丢失了

(2) 脑裂导致的数据丢失

脑裂,也就是说某个master所在机器突然脱离了正常的网络,跟其他slave机器不能连接,但是实际上master还运行着。此时哨兵可能就会认为master宕机了,然后开启选举,将其他slave切换成了master。这个时候,集群里就会有两个master,也就是所谓的脑裂。

此时虽然某个slave被切换成了master,但是可能client还没来得及切换到新的master,还继续写向旧master的数据可能也丢失了。因此旧master再次恢复的时候,会被作为一个slave挂到新的master上去,自己的数据会清空,重新从新的master复制数据。

2、解决异步复制和脑裂导致的数据丢失

由于Redis是异步复制的,因此网络分区的情况下无法完全防止数据丢失,但是可以使用以下Redis配置选项来限制数据的差异:

#要求至少有1个slave,数据复制和同步的延迟不能超过10秒
#如果一旦所有的slave,数据复制和同步的延迟都超过了10秒钟,那么这时master就不会再接收任何请求了
min-slaves-to-write 1
min-slaves-max-lag 10

上面两个配置可以减少异步复制和脑裂导致的数据丢失

(1) 减少异步复制的数据丢失

min-slaves-max-lag这个配置,可以确保一旦slave复制数据和ack延时太长,就认为可能master宕机后损失的数据太多了,那么就拒绝写请求,这样可以把master宕机时由于部分数据未同步到slave导致的数据丢失降低的可控范围内

(2) 减少脑裂的数据丢失

如果一个master出现了脑裂,跟其他slave丢了连接,那么上面两个配置可以确保说,如果不能继续给指定数量的slave发送数据,而且slave超过10秒(min-slaves-max-lag 参数)没有给自己ack消息,那么就直接拒绝客户端的写请求。这样脑裂后的旧master就不会接受client的新数据,也就避免了数据丢失。

上面的配置就确保了,如果跟任何一个slave丢了连接,在10秒后发现没有slave给自己ack,那么就拒绝新的写请求。因此在脑裂场景下,最多就丢失10秒的数据。

三、cluster集群

官方文档:

Redis cluster tutorial(Redis 集群教程)

Redis Cluster Specification(Redis集群规范)

1.一致性hash算法

常规的hash算法在分布式中存在动态伸缩的问题,一致性hash算法的提出正是用来解决该问题的。(常规的hash取模算法是对服务器的数量进行取模,而一致性hash算法是对232取模。服务器数量是可变的,而232是个常量)

一致性hash算法将节点与数据映射到一个抽象出来的哈希环上,哈希环的范围是0-232。存储节点可以根据ip地址进行hash运算,再对232取模,得到的值一定是在0-232这个范围之内,也就是该节点映射到环上的位置。分布式中的多个节点都是按照这样的方式映射到了哈希环上。

hash(服务器的IP) % 2^32

而数据则是使用同样的hash函数运算并取模,落到环上的某个位置,然后沿着顺时针移动,遇到的第一个节点就是该数据最终要存入的节点

1)添加节点

如果分布式系统又加入了新的节点,有什么影响呢?

以上图为例,此时已经有4个节点通过上面的方式映射到了环上,现在又来了一个节点5。通过hash运算映射到下图中的位置上。此时,就需要将原先定位在节点2和节点5间的数据从节点4迁移到节点5上,这样才不会影响后续对这些数据的访问。

2)删除节点 

当删除节点时,那么该节点上的数据就会被重新分配到它顺时针的下一个节点上。下图中当删除节点2时,节点2的数据被分配到了节点4上。

这里存在一个问题,节点4的负载就会增加。如果节点2上有热点数据,就有可能将节点4直接打挂,这样就会顺时针再将数据转移到节点3,最终导致所有节点雪崩

另外还有另外一个问题,当集群中节点过少时,还可能出现数据倾斜:大量的数据都被分配到了同一个节点上,导致单一节点访问量很高,造成流量不均衡。如下图所示,从B顺时针到C明显比C顺时针到A的范围小,那么数据被分配到A的概率就会比C大的多。

【常规hash算法】的缺点是存在动态伸缩问题:当添加或删除节点时,所有节点上的数据都要重新分配。

【一致性Hash算法】在增减节点时都只需重新分配环空间中的一小部分数据,具有较好的容错性和可扩展性。
但缺点是,存在数据倾斜问题:当节点太少时,容易因为数据分布不均匀而造成数据集中在一台服务器上。

虚拟节点

为了解决循环崩溃和数据倾斜问题,提出了虚拟节点的概念。就是将真实节点计算多个哈希值,每个计算结果对应的环上的位置都放置一个此节点,也就是把原节点分为多个虚拟节点了。如下图所示,每个节点都映射出了两个虚拟节点,当节点A挂掉以后,A的数据就会被重新分配到B1和C2中,而并没有全部转移到同一个节点上,也就解决了雪崩问题;此外,由于引入虚拟节点而使环中的节点数量增多,不同的节点的虚拟节点是交互分布的,所以数据倾斜到某一个节点的概率大大降低了。

2.哈希槽(hash slot)

Redis Cluster集群并没有使用一致性hash, 而是引入了哈希槽的概念. 

Redis集群有16384个哈希槽,每个key通过CRC16校验(CRC16能很好地把不同的键均匀地分配到 16384 个槽中),然后对16384取模来决定放置哪个槽。

CRC16(key) % 16384

集群的每个节点负责一部分hash槽。比如当前集群有3个节点,那么:

  • 节点 A 包含 0 到 5500号哈希槽.
  • 节点 B 包含5501 到 11000 号哈希槽.
  • 节点 C 包含11001 到 16384号哈希槽.

这种结构很容易添加或者删除节点。例如,要想添加一个新节点D,则需要将一些哈希槽从节点A,B,C移到D。类似地,要删除节点A,则只需移动A所负责的哈希槽到B和C,当节点A为空时就可以将其从群集中完全删除。由于将哈希槽从一个节点移到另一个节点并不停需要止服务,所以无论添加删除节点,或者改变某个节点的哈希槽的数量都不会造成集群不可用。

用户可以通过使哈希标签来强制多个key成为同一哈希槽的一部分。只要单个命令执行(或整个事务或Lua脚本执行)中涉及的所有键都属于同一个哈希槽Redis Cluster就支持多种键操作。

哈希标签记录在Redis Cluster规范中,但是要点是,如果key的{}括号之间有一个子字符串,则仅对字符串中的内容进行哈希处理,例如,此{foo}键和另一个{foo}键保证会在同一哈希槽中,并且可以在以多个key作为参数的命令中一起使用。

数据一致性问题(数据丢失问题)

Redis Cluster无法保证强一致性。这意味着在某些情况下,Redis Cluster可能会丢失写操作。有下面来两方面的原因:

1)Redis使用异步复制

这意味着在写入期间会发生以下情况:

  • 客户端写入Master B
  • B回复OK
  • B将写传播到从机Slave B1,B2,B3

如果B在还未将写传播到Slave时发生了崩溃,Slave中的一个会被提升为Master。而新的Master由于还未收到写,所以会永远丢失写操作。

2)存在网络分区

如果一个集群有6个节点:A, B, C, A1, B1, C1,三主三从,且有一个客户端Z1。现在发生了网络分区,使得Z1和B处于同一个分区,其它的节点在另一个分区。

此时,Z1仍然能够写入B。如果分区在很短的时间内恢复正常,则群集将继续正常运行。但是,如果分区持续的时间较长,使得B1升级到该分区的多数端上的Master,则Z1发送给B的写操作将丢失。

Z1将能够发送给B的写入量有一个最大的窗口:如果已经有足够的时间让分区的多数派选举出一个从属主机,则少数派的每个主节点都将停止接受写。

此时间量是Redis Cluster的一个非常重要的配置指令,称为节点超时节点超时过去之后,主节点将被视为发生故障,并且可以用其副本之一替换。类似地,在没有主节点能够感知大多数其他主节点的情况下经过了节点超时之后,它进入错误状态并停止接受写操作。

3.一致性hash和redis hash槽的区别

1)redis hash槽并不是闭合的,它一共有16384个槽,使用CRC16算法计算key的hash值,与16384取模来确定数据在哪个槽中,从而找到所属的redis节点;一致性hash表示一个0到2^32的圆环,对数据计算hash后落到该圆环中,顺时针第一个节点为其所属服务。

2)一致性hash是通过虚拟节点去避免服务节点宕机后数据转移造成的服务访问量激增、内存占用率过高、数据倾斜等问题,保证数据完整性和集群可用性的;而hash集群是使用主从节点的形式,主节点提供读写服务,从节点进行数据同步备份,当主节点出现故障后,从节点继续提供服务。

(中华石杉)
关于集群的补充:
1.Redis主从+哨兵的部署架构只保证高可用,并不保证数据零丢失,也就是可能丢数据。建议尽量在测试环境和生产环境,都进行充足的测试和演练。 2.前些年redis的分布式部署需要借助中间件来实现,比如codis或twemproxy,其中codis用的非常多。现在推荐直接使用redis官方的redis cluster。可以参考:那些年用过的Redis集群架构
经验值: 1.单台redis支持的并发一般在10万(理论),一般四五万已经算很高了。 2.中小公司使用哨兵模式已经足够了。
不积跬步,无以至千里。不积小流,无以成江海!
原文地址:https://www.cnblogs.com/rouqinglangzi/p/10567337.html