一、单机模式

单机模式架构图如下:

单机模式比较好理解,就是整个系统中只有一个redis节点,需要为所有连接的客户端提高读写服务,在小型项目中通过采用单机模式就可以正常工作。但是在中大型的项目架构中,单节点就会有一些问题会暴露出来。

1、内存瓶颈,数据量大时一台节点的内存无法存储所有的数据

2、IO瓶颈,客户端数量较多时,同时处理的客户端数量有限,且是单线程处理,无法同时处理太多的客户端请求

3、可用性不高,一旦单节点宕机,就会导致整个redis不可用

单机模式的使用和原理不复杂,无需过多赘述。

二、主从模式

主从模式是在单机模式的基础之上,增加了N个从节点,主节点主要负责客户端的写操作,从节点则负责客户端的读操作,另外从节点需要实时同步从节点的写操作,以保证主从节点的数据一致性。

优点:读写分离,读的压力分散到从节点,主节点宕机从节点仍然可以提供读服务

缺点:

1、主节点依然承受所有的写压力;

2、每个节点的数据都一模一样,可存储数据的总量和单机一样,依然没有解决单节点的数据容量问题;

3、主机宕机,宕机前的未同步到slave节点的数据会丢失

4、主机宕机,需要手动配置新的master节点

 

2.1、主从模式的实现

小编测试机器有限,是在虚拟机上通过yum安装了redis服务,首先通过systemctl redis start 启动master节点的服务,接下来就是启动从节点服务的步骤

1、首先复制配置redis.conf文件,重命名为redis-slave1.conf

2、修改redis-slave1.conf中的内容,主要修改内容如下:

# 配置监听的端口号(和master节点区分开)
port 7000

# 副本配置, 如果当前节点属于从节点,通过配置masterip和masterport配置主节点的ip和端口
slaveof localhost 6379

# 如果主节点配置了密码校验,那么通过配置masterauth配置主节点的密码
masterauth  master节点密码(如果有的话)

# 从节点是否只读,默认配置yes表示从节点只读;如果配置no表示从节点可写,但是不建议从节点可写
slave-read-only yes

 

核心配置实际就两个,一个是修改端口号避免和master节点冲突(不同节点无所谓),然后就是添加master节点的配置,slaveof master节点IP  master节点端口号

3、启动redis服务,找到redis-server启动脚本,按指定配置文件执行

/usr/bin/redis-server   /etc/redis-slave1.conf & 

4、启动成功之后,可以通过redis可视化管理工具查看,可以看到从节点会自动将主节点上的所有数据同步过去,并且后续主节点数据更新,会同步到从节点

另外从节点配置了slave-read-only yes之后表示从节点只可读不可写,如果向从节点写执行写操作,会直接抛异常:READONLY You can't write against a read only slave.

2.2、主从模式的工作原理

1、主节点启动,提供正常的读写服务

2、从节点启动,和主节点建立TCP连接

3、从节点向主节点发生psync命令表示需要同步数据

4、主节点接收到psync之后执行bgsave命令生成RDB内存快照文件

5、主节点的RDB文件生成完成之后通过TCP连接发送给从节点,并且将之后的写命令存入缓存区

6、从节点接收到RDB文件之后开始写入本地内存中

7、主节点将缓冲区的写命令发生给从节点,从节点执行写命令(此时主从节点数据保持了一致)

8、主节点在执行写命令时会将写命令同步给从节点

三、哨兵模式

3.1、哨兵模式简介

Sentinel(哨兵)用于监控redis集群中节点的运行状态,通过监控master和slave节点的状态,并通过故障转移的方式保证了redis集群的高可用性。Sentinel系统通常会包含多个sentinal实例,因为单个哨兵可能会由于本身的原因而出现误判的情况。所以通常一个哨兵系统至少应该需要有三个sentinal实例。哨兵主要有以下三个作用:

1、监控:通过不停的向master和slave节点以及其他Sentinal发送ping,来监控其他节点运行状态

2、提醒:当监控的Redis节点出现异常,会通过API向其他程序发生通知

3、故障转移:当Sentinal系统检测某个master不可用时,Sentinal系统会进行一次故障转移操作,将故障的master节点的其中一个slave节点升级为新的master,并通知其他的slave节点更新master节点信息为新的master。原因的master节点会变成slave节点。

当客户端访问已经故障的master节点时,集群会返回新的master节点给客户端使用。

3.2.、哨兵模式原理

1、每个哨兵会每秒向监控的master、slave以及其他Sentinel节点发生ping命令,确保监控的节点正常运行

2、如果一个实例回复距离最后一次有效回复ping命令的时间超过down-after-milliseconds配置的值,就会被Sentinal标记为主观下线

3、如果一个master被标记主观下线,则正在监视这个master的所有Sentinal都以每秒ping一次来确认master已经主观下线

4、当足够数量的Sentinal(超过一半)都认为master以及下线,那么Master就会最终被标记为客观下线,也就是在集群中以及被确认不可用了

5、每个Sentinal会以每10秒一次向所有的master、slave节点发生INFO命令

6、当Master被Sentinel标记客观下线后,Sentinel向下线的Master对应的所有Slave发送INFO命令会从10秒一次改成1秒一次

7、如没有足够数量的Sentinel认为master已经下线,Master的客观下线状态会被移除。且活Master重新下Sentinel的ping命令返回有效回复,主观下线状态也会被移除

3.3、故障转移的步骤

1、从客观下线的master节点的所有slave节点中挑选一个新的Master节点

2、向所有slave节点发送新的Master节点的信息,所有Slave节点更新Master节点信息

3、下线的Master节点变成新Master节点的Slave节点,恢复正常时会变成Slave节点

四、集群模式

 

集群模式最大的区别就在于master节点不止一个,而是可以有N个,每个master节点再分配N个slave节点,这样就相当于集群模式是有N个主从模式构成的。每个master节点存储的数据不一样,这样就间接的解决了单机master节点的存储容量问题,和达不到高可用的问题。集群模式下可以有多个master节点,存储容量相当于翻倍,另外某一个master节点宕机不可用,其他master节点仍然继续可以提供工作,使得整个集群仍然处于高可用状态。

4.1、集群模式的使用

集群模式通常为了提供高可用性,至少需要3个master节点和3个slave节点

1、复制redis.conf文件,依次复制成3个master的配置和3个slave的配置

2、master的conf配置文件主要需要配置集群信息,配置cluster-enabled则表示为开启了集群模式

1 #开启redis的集群模式
2 cluster-enabled yes
3 
4 #配置集群模式下的配置文件(依具体的端口号为准)
5 cluster-config-file nodes-6379.conf
6 
7 #集群内节点之间支持最长响应时间
8 cluster-node-timeout 15000

 

3、slave的conf配置主要配置各自的master节点信息即可,可参考主从模式配置方案

4、依次执行命令启动各个redis服务,启动3个master服务和3个slave服务

/usr/bin/redis-server  /etc/redis1.conf. &
/usr/bin/redis-server  /etc/redis2.conf. &
/usr/bin/redis-server  /etc/redis3.conf. &
/usr/bin/redis-server  /etc/redis-slave1.conf. &
/usr/bin/redis-server  /etc/redis-slave2.conf. &
/usr/bin/redis-server  /etc/redis-slave3.conf. &

 

5、执行结果如下,通过ps -ef | grep 'redis' 可以看出3个master节点组成了redis集群

 

6、此时通过任意一个master节点访问key,都会报错,提示如下:

 

虽然现在已经启动了3个master节点,但是这3个节点之间还没有存在任何的联系,此时就需要用到redis官方提供的redis-trib.rb工具进行集群环境的搭建,首先就需要安装ruby运行环境

1、通过ruby --version检测是否安装了ruby工具,如果没有则安装ruby,命令如下:

yum install ruby

yum install runbygems

然后安装redis集群需要的插件

gem install redis

结果提示如下:

[root@centos-7 bin]# gem install redis Fetching: redis-4.2.2.gem (100%) ERROR: Error installing redis: redis requires Ruby version >= 2.3.0.

由于通过yum安装的ruby为2.0.0版本,而redis集群至少需要ruby的版本为2.3.0版本,所以需要安装高版本的ruby,至少需要安装到2.3.0版本

2、安装成功之后继续执行gem install redis命令

3、创建redis集群

./redis-trib.rb create --replicas 1 10.211.55.3:6379 10.211.55.3:7001  10.211.55.3:7002

此时redis集群已经创建成功

 

4.2、redis集群的工作原理

4.2.1、hash槽的分配

redis集群将存储工具分隔成2的14次方个也就是16384个哈希槽(slot),集群中的每个master节点会平均分配哈希槽。如3个master节点就会依次分配slot位置为:0~5460、5461~10922、10922~16383。所以redis集群中的master节点最多不能超过哈希槽的总个数16384个。redis集群中存取key时,会先通过CRC16算法将key进行计算得到一个hash值,然后将hash值对16384进行取模运算,这样key就会被分配给对应的hash槽对应的master节点上。

每个master节点启动的时候都会被分配部分hash槽

新增节点时:将其他节点的部分hash槽分配给新节点

删除节点时:将被删除节点的hash槽分配给其他节点

每个hash槽相当于是一个数据分区

4.2.2、节点之间的通信

redis集群中所有的master节点都会通过gossip协议进行通信,redis集群并非是集中式管理,也就是没有集中的地方存储所有的redis集群master信息,而是每个redis master节点包包含了整个集群的信息。

集中式的好处是数据只有一份,可以保证数据的一致性,并且更新的时候只需要更新一份即可,但是元数据频繁更新会导致中心压力较大

gossip:元数据分散存储,元数据更新时有一定的延时。但是元数据更新是分散更新,降低了更新的压力

1、节点通信

redis集群中每个节点都会开放一个给客户端的端口,比如默认6379,那么和其他节点之间通信的端口就会在给客户端端口数字上+10000,比如给客户端的端口为7000,那么和其他节点通信用的端口就是17000

每个节点之间每隔一段时间就会互相发送ping消息,各个节点接收到ping消息之后会回复pong消息。通过ping+pong机制达到心跳的监控效果。

另外每个节点之间会不断的交换各自的元数据信息,如:故障信息、节点的增加和移除、哈希槽信息等。

2、ping消息

每个节点互相发送ping信息,ping信息由于发送比较频繁会增加网络负担,每个节点每秒会发送10次ping,每次会选择最多5个最久没有通信的其他节点。如果发现某个节点通信延时达到了cluster_node_timeout/2,那么立即发送ping,避免数据不一致

ping消息会带上本身节点的信息,并且会带上最少3个,最多总节点数-2个其他节点的信息用于同步。

4.2.3、客户端请求重定向

客户端向redis集群中发送命令并不是发送给所有master节点,而是任意挑选一个master节点发送命令,master节点先根据key通过CRC16算法计算key,再计算对应的哈希槽。如果属于当前节点就直接执行,否则就将计算的哈希槽对应的节点地址告诉客户端,

客户端重定向到对应的节点上去执行命令。每个master节点都知道其他节点所负责的哈希槽信息

由于客户端会重定向,所以会额外增加一次网络通信的消耗,所以针对客户端可以进行优化,通过客户端自己就能先计算哈希槽,并且知道整个集群中各个节点负责的哈希槽,那么就可以直接发送命令到对应的节点上,这样就不需要再走一次重定向了。

4.2.4、JedisCluster的工作原理

JedisCluster初始化的时候,由于每个master节点都保存了整个集群的哈希槽信息和每个节点负责的哈希槽,所以JedisCluster初始化的时候会随机选择一个Master节点,获取哈希槽信息和节点哈希槽映射关系,存在本地缓存。

同时JedisCluster会给每个Master节点都创建一个JedisPool连接池,每个JedisCluster执行操作,首先根据key计算对应的哈希槽,在本地找到对应的节点信息,然后直接发送到对应的节点执行命令即可。

但是由于JedisCluster本地缓存可能已经数据不准了,可能在JedisCluster初始化之后redis集群信息已经发送了改变了,那么先向本地找到的节点发送命令,如果节点返回了需要重定向,那么先通过这个节点更新下本地的哈希槽缓存信息。

再重新计算一次找到对应的节点。如果重复操作超过了5次还是失败,那么就直接抛异常了。

4.2.5、集群模式故障处理

由于主从模式一旦master节点故障,从节点只能提供读服务或者手动将slave节点升级为master节点,所以增加了哨兵模式,通过哨兵监控master节点的状态,一旦master故障就选取slave节点选举为新的master节点。

而集群模式下已经兼容了主从模式和哨兵模式了。集群模式下高可用性和主备切换原理如下:

1、判断master节点宕机

集群中如果某个master判断另一个master宕机,那么就标记为主观下线,如果多个节点都认为这个节点宕机,那么就会判断为客观下线,也就是在整个集群中不可用了

每个节点通过判断ping、pong的时间是否超过cluster-node-timeout判断是否下线,如果判断下线就会在ping消息中通知其他节点交换信息,超过一半的节点都认为主观下线了就会被判定为客观下线

2、从节点过滤

master宕机需要从这个master节点的所有slave节点中挑选新的master,会检查每个slave与master断开连接的时间,如果超过了cluster-node-timeout * cluster-slave-validity-factor时长,表示和master断开时间过长,会存在数据落后的情况,那么就没有资格称为新的master。

3、从节点选举

哨兵:对所有从节点进行排序,slave priority,offset,run id

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

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

基本上redis cluster和哨兵模式的流程一样,相当于集群模式就自行具备了哨兵模式的功能。

 

Tips:

集群模式和哨兵模式的区别?

1、哨兵模式将监控的功能交给所有的哨兵;集群模式下监控的功能是由所有的master节点监控

2、故障转移时哨兵模式会选举一个leader哨兵来进行故障迁移工作;集群模式下会选举一个新的master节点来负责故障迁移工作