Redis 分布式

为什么需要Reids 集群?

性能

Redis 本身的QPS 已经很高了,但是如果在一些并发量非常高的情况下,性能还是会受到影响的。这个时候我们就会希望能有更多的Redis服务来完成工作。

扩展

出于对于存储的考虑。因为Redis 所有的数据都放在内存中,如果数据量大,很容易受到硬件的限制。升级硬件收效和成本比太低,所以我们就会需要一种横向的扩展的方法。

可用性

第三个是可用性和安全的问题。如果只有一个Redis服务,一旦服务宕机,那么所有客户端都无法访问,回会业务造成很大的影响。另一个,如果硬件发生故障,而单击的数据无法恢复的话,带来的影响也是灾难性的。

可用性,数据安全、性能都可以通过搭建多个Redis 服务实现。其中有一个是主节点,可以有多个从节点。主从之间通过数据同步,存储完全相同的数据。如果主节点发生故障,则把某个从节点改成主节点,访问新的主节点。

Redis 主从赋值

主从赋值的配置

例如一主多从,101 是主节点,在每个slave 节点的redis.conf 配置文件增加以上

slaveof 192.168.1.101 6379

在主从切换的时候,这个配置会被重写成:

./redis-server --salveof 192.168.1.101 6379

或者在客户端直接执行 slaveof xx xx,使得Redis 实例称为从节点。启动后,查看集群状态:

redis> info replication

从节点不能写入数据(只读),只能从master 节点同步数据。get成功,set失败,

主节点写入后,slave 会自动从master 同步数据:

断开复制:

redis > slaveof no none

此时从节点会变成自己的主节点,不再复制数据。

主从赋值的原理

连接阶段

1、salve node 启动时(执行 slaveof 命令),会自己本地保存 master node 的信息,包括master node  的host 和ip。

2、slave node 内部有个定时任务 replicationCron,每隔 1秒钟 检查是否有新的 master node 要连接和赋值,如果发现,就跟master node 建立socket 网络连接,如果连接成功,从节点为该 socket 建议一个专门处理复制工作的文件事件处理器,负责后续的复制工作,如接受RDB文件,接受命令传播等。

数据同步阶段

1、master node 第一次执行全量复制,通过bgsave 命令在本地生成一份 RDB文件,将RDB快照文件发给slave node(如果超时会重连,可以调大 repl-timeout的值)。slave node 首先清除自己的旧数据,然后用RDB文件加载数据,

问题:生成RDB期间,master 接收到的命令怎么处理?

开始生成RDB文件时,master 会把所有新的写命令缓存在内存中。在slave node保存了RDB之后,再将新的命令复制给slave node。

命令传播阶段

1、master node 持续将写命令,异步复制到slave node

延迟是不可避免的,只能通过优化网络

repl-disable-tcp-nodelay no

当设置成yes 时,TCP会对包进行合并从而减少带宽,但是发送的频率会降低,从节点数据延迟增加,一致性变差;具体发送频率与linux 内核的配置有关,默认配置为 40ms。当设置成no时,TCP会立马将主节点的数据发送给从节点,带宽增减但是延迟变小。

一般来说,只有当应用对Redis 数据不一致的容忍度较高,且主从节点之间网络状况不好时,才会设置为yes,多数情况使用默认值no。

假如,从节点有一段 时间断开了与主节点的连接是不是要重新全量复制一遍?如果可以增量复制,怎么知道上次复制到哪里呢?

通过master_repl_offset 记录偏移量 。

redis > info replication

主从赋值的不足

主从模式解决了 数据备份和性能(通过读写分离)的问题,但是还是存在一些不足:

1、RDB文件过大的情况下,同步非常耗时。

2、在一主一从或者一主多从的情况下,如果主服务器挂了,对外提供的服务就不可用了,单点问题没有得到解决。如果每次都是手动把之前的从服务器切换成主服务器,这个比较费时费力,还会造成一定时间的服务不可用。

可用性保证之哨兵机制

哨兵机制的原理

如何实现主从的自动切换

创建一台监控服务器来监控所有Redis服务节点的状态,比如,master 节点超过一定时间没有给监控 服务器发送心跳报文,就把master 标记为下线,然后把某一个slave 变成master 。应用每一次 都是从这个监控服务器拿到master 的地址。

  • 但是,如果监控服务器本身出现了问题怎么办?

Redis 的Sentinel 就是这种思路:通过运行监控服务器来保证服务的可用性。

从Redis 2.8版本起,提供了一个稳定版本的 Sentinel (哨兵),用来解决高可用的问题,他是一个特殊状态的redis 实例

我们会启动一个或者多个Sentinel 的服务(通过 src/redis-sentinetl)。它本质上只是一个运行在特殊模式之下的Redis,Sentinel 通过info 命令 得到被监控Redis 机器的master,slave 等信息。



redis客户端心跳检测 redis心跳机制_socket心跳机制图片

为了保证监控服务器的可用性,我们会对Sentinel做集群的部署。Sentinel 既监控所有的Redis 服务,Sentinel 之间也可以相互监控。

注意:Sentinel 本身没有主从之分,只有Redis 服务节点有主从之分。

服务下线

Sentinel 默认以每秒钟1次的频率向Redis服务节点发送PING命令。如果在down-after-milliseconds 内都没有收到有效回复,Sentinel 会将该服务器标记为下线(主观下线)

# sentinel.confsentinel down-after-milliseconds

这个时候sentinel 节点会继续询问其他的Sentinel 节点,确认这个节点是否下线,如果多数 Sentinel 节点都认为master 下线,master 才真正确认被下线(客观下线),这个时候就需要重新选举master

故障转移

如果master 被标记为下线,就会开始故障转移流程。

故障转移流程额第一步就是在Sentinel 集群选择一个Leader ,由Leader 完成故障转移流程,Sentinel 通过Ratf算法,实现Sentinel 选举。

「Ratf算法」

在分布式存储系统中,通常通过维护多个副本来提高系统的可用性,那么多个节点之间必须要面对数据一致性的问题。Raft 的目的就是通过复制的方式,使得所有节点达成一致,但是这么多的节点,以哪个节点的数据为准呢?所以必须选出一个Leader。

大体上有两个步骤:领导选举,数据赋值。

Raft 是一个共识算法,其核心思想:先到先得,少数服从多数

Raft算法演示地址:http://thesecretlivesofddata.com/raft/

总结:

1、master 客观下线触发选举,而不是通过election timeout 时间开始选举

2、Leader 并不会把自己成为leader 的消息发给其他Sentinel。其他Sentinel 等待Leader 从slave 选出master 后,检测到新的 master 正常工作后,就会去掉客观下线的标识,从而不需要进入故障转移流程。

那怎么让一个原来的slave 节点 成为主节点?

1、选出Sentinel Leader 之后,由Sentinel Leader 向某个节点发送 slaveof no one 命令,让他成为独立节点。

2、然后向其他节点发送 slaveof x.x.x.x xxxx (本机服务),让他们成为这个节点的子节点,故障转移完成。

那么这么多的从节点,应该选举哪个节点成为主节点呢?

关于从节点的选举,一共有四个因素影响选举的结果,分别是断开连接时长,优先级排序,复制数量,进程id

如果与哨兵断开连接的比较久,超过了某个阈值,就直接失去了选举权,如果拥有选举权,那就看谁的优先级高,这个在配置文件里可以设置(replica-priority 100),数值越小优先级越高。

如果优先级相同,就看谁从master 中复制的数据最多(复制偏移量最大),选最多的那个,如果复制数量也相同,就选择进程id 最小的那个。

Sentinel 功能总结

监控:Sentinel 会不断检查主服务器和从服务器是否正常运行

通知:如果某一个监控的实例出现问题,Sentinel 可以通过API发出通知。

自动故障转移:如果住服务器发生故障,Sentinel 可以启动故障转移过程,把某太服务器升级为主服务器,并发出通知。

配置管理:客户端连接到Sentinel ,获取当前的Redis 主服务器的地址。

Sentinel 实战

Sentinel 配置

为了保证Sentinel 的高可用,Sentinel 也需要做集群部署,集群中至少需要三个Sentinel 实例(推荐奇数个,防止脑裂)。

hostname

IP地址

节点角色和端口

master

192.168.1.101

Master:6379/Sentinel:26379

slave1

192.168.1.102

Slave:6379/Sentinel:26379

slave2

192.168.1.103

Slave:6379/Sentinel:26379

在 102和103 的 src/redis.conf 配置文件中添加:

slaveof 192.168.1.102 6379

在 101,102,103 创建 sentinel配置文件(安装后根目录下默认有sentinel.conf):

cd /redis-5.0.5mkdir logsmkdir rdbsmkdir sentinel-tmpvim sentinel.conf

三台服务器内容相同:

daemonize yesport 26379protected-mode nodir "/data/redis-5.0.5/sentinel-tmmp"sentinel monitor redis-mmaster 192.168.1.101 6379 2sentinel down-after-milliseconds redis-master 30000sentinel failover-timeout redis-master 180000sentinel parallel-syncs redis-master 1

上面出现了 4 个 redis-master ,这个名称要统一,并且使用客户端连接的时候名称要正确

hostname

IP地址

protected-mode

是否允许外部网络访问

dir

sentinel的工作目录

sentinel monitor

sentinel监控的redis主节点

down-after-milliseconds(毫秒)

master宕机多久,才会被sentinel主观认为下线

sentinel failover-timeout(毫秒)

1、同一个 sentinel 对同一个 master 两次failover 之间的间隔时间                               2、当一个slave从一个错误的master那里同步数据开始计算时间。这道slave被纠正为正确的master那里同步数据时。                                                                                           3、当想要取消一个正在进行的failover 所需要的时间                                                      4、当进行 failover 时,配置所有slaves 指向新的 master 所需的最大时间。

parallel-syncs

这个配置项指定了在发生failover主备切换时最多可以有U盾讴歌slave 同时对新的master进行同步,这个数字越小,完成failover 所需的时间就越长,但是如果这个数字越大,就以为着越多的slave 因为replication 而不可用。可以通过将这个值设置为1来保证每次只有一个slave 处于不能处理命令请求的状态。

Sentinel 验证

启动Redis 服务和Sentinel

cd /data/redis-5.0.5/src# 启动Redis 节点./redis-server ../redis.conf# 启动sentinel 节点./redis-sentinel ../sentinel.conf# 或者./redis-server ../sentinel.conf --sentinel

查看集群状态的命令:

redis > info replication

模拟master  宕机,在 101 执行:

redis > shutdown
Sentinel 连接与使用

Jedis 连接Sentinel

private static  JedisSentinelPool createJedisPool(){    String masterName = “redis-master”;    Set sentinels = new HashSet();    sentinels.add("192.168.1.101:26379);    sentinels.add("192.168.1.102:26379);    sentinels.add("192.168.1.103:26379);    pool = new JedisSentinelPool(masterName,sentinels);    return pool;}

String Boot 连接 Sentinel :

spring.redis.sentinel.master = redis-masterspring.redis.sentinel.nodes=192.168.1.101:26379,192.168.1.102:26379,192.168.1.103:26379

无论是Jedis 还是还是Spring Boot ,都只需要配置全部哨兵地址,由哨兵地址返回当前的 master 节点地址。

哨兵机制的不足

主从切换的过程中会丢失数据,因为只有一个master。

只能单点写,没有解决水平扩容的问题。

如果数据量非常大,这个时候我们需要多个 master-slave 的group,把数据分不到不同的 group 中。