【大厂面试】面试官看了赞不绝口的Redis笔记(三)分布式篇

说明

因为内容太多了,CSDN编辑器不允许我在一篇文章上发挥。这是头两篇。

介绍了Redis单机的方方面面

传送门:【大厂面试】面试官看了赞不绝口的Redis笔记
目录:
在这里插入图片描述
传送门:【大厂面试】面试官看了赞不绝口的Redis笔记(二)
目录:
在这里插入图片描述

下面我们步入分布式领域

六、Redis复制的原理与优化

(一)Redis单机的问题存在三方面的问题

(1)机器故障

在一台服务器上部署一个Redis节点,如果机器发生主板损坏,硬盘损坏等问题,不能在短时间修复完成,就不能处理Redis操作了,这会带来很大的麻烦。
但是即使服务器正常运行,Redis主进程也有可能发生宕机事件,一般重启Redis就可以。如果不考虑在Redis重启期间的性能损失,可以考虑Redis的单机部署。如果你想解决这种问题,可以考虑Redis分布式部署。

(2)容量瓶颈

一台服务器有16G内存,此时分配12G内存运行Redis

如果有新需求:Redis需要占用32G或者64G等更多的内存,此时这台服务器就不能满足需求了,此时可以考虑更换一台更大内存的服务器,越大型的服务器,价格也就越高。分布式允许多台服务器组成一个Redis集群来满足这个需求。

(3)QPS瓶颈
根据Redis官方的说法,单台Redis可以支持10万的QPS,如果现在的业务需要100万的QPS,此时可以考虑使用Redis分布式,提高性能。

这样就引出了下面的主角 – 主从复制

(二)主从复制(主从同步)

在了解 Redis 的主从复制之前,你需要一点分布式的理论基础 【分布式】关于分布式“一致性”的讨论,这篇文章因为转载内容过多,设置为转载了,算是我整合的一篇文章。

你要是想能有更加体系化的学习分布式,可以看这本书。放心zookeeper对于大多数开发者而言,都是需要掌握的。
在这里插入图片描述

1.主从复制的模型:

(1)一主一从模型

一个Redis节点为master节点(主节点),负责对外提供服务。另一个节点为slave节点(从节点),负责同步主节点的数据,以达到备份的效果。

当主节点发生宕机等故障时,从节点也可以对外提供服务

如下图所示
在这里插入图片描述
(2)一主多从模型

一个Redis节点为master节点(主节点),负责对外提供服务。多个节点为slave节点(从节点),每个slave都会对主节点中的数据进行备份,以达到更加高可用的效果。

这种情况下就算master和一个slave同时发生宕机故障,其余的slave仍然可以对外读提供服务,并保证数据不会丢失

当master有很多读写,达到Redis的极限阀值,可以使用多个slave节点对Redis的读操作进行分流,有效实现流量的分流和负载均衡,所以一主多从也可以做读写分离(master节点负责写数据,同时客户端可以从slave节点读取数据)

在这里插入图片描述

主从复制作用:对数据提供了多个备份,这些备份数据可以大大提高Redis的读性能,是Redis高可用或者分布式的基础

3.主从复制的配置
有两种方式,一种是命令行,一种是配置文件。

(1)命令行
slaved命令
在这里插入图片描述
取消复制
在这里插入图片描述
(2)配置文件配置
修改Redis配置文件/etc/redis.conf

slaveof <masterip> <masterport>         # masterip为主节点IP地址,masterport为主节点端口
slave-read-only yes                     # 从节点只做读操作,不做写操作,保证主从设备数据相同

(3)两种主从配置方式比较

  • 使用命令行配置无需重启Redis,可以实现统一配置
  • 使用配置文件方式配置不变于管理,而且需要重启Redis

(三)全量复制和部分复制

(一)两个概念

1.run_id的概念

Redis每次启动时,都有一个随机ID来标识Redis,这个随机ID就是上面通过info命令查看得到的run_id
查看192.168.81.101虚拟机上的run_id和偏移量

[root@localhost ~]# redis-cli info server |grep run_id
run_id:7e366f6029d3525177392e98604ceb5195980518
[root@localhost ~]# redis-cli info |grep master_repl_offset
master_repl_offset:0

查看192.168.91.100虚拟机上的run_id和偏移量

[root@lalalala ~]# redis-cli info server | grep run_id
run_id:7091f874c7c3eeadae873d3e6704e67637d8772b
[root@lalalala ~]# redis-cli info | grep master_repl_offset
master_repl_offset:4483

在上面的例子里,192.168.81.101作为slave去复制192.168.81.100这个master上的数据,会先获取192.168.81.100机器上对应的run_id,并在192.168.81.101上做一个标识

当192.168.81.100机器上的Redis的run_id发生改变,意味着192.168.81.100机器上的Redis发生重启操作或者别的重大变化,192.168.81.101就会把192.168.81.100上的数据全部同步到192.168.81.101上,这就是全量复制的概念

2.offset的概念

偏移量(offset)就是数据写入量的字节数。

在192.168.81.100的Redis上写入数据时,master就会记录写了多少数据,并记录在偏移量中。

在192.168.81.100上的操作,会同步到192.168.81.101机器上,192.168.81.101上的Redis也会记录偏移量。

当两台机器上的偏移量相同时,代表数据同步完成

偏移量是部分复制很重要的依据

查看192.168.81.100机器上Redis的偏移量

127.0.0.1:6379> info replication    # 查看复制信息
# Replication
role:master
connected_slaves:1
slave0:ip=192.168.81.101,port=6379,state=online,offset=8602,lag=0
master_repl_offset:8602             # 此时192.168.81.100上的偏移量是8602
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:8601
127.0.0.1:6379> set k1 v1           # 向192.168.81.100写入数据
OK
127.0.0.1:6379> set k2 v2           # 向192.168.81.100写入数据
OK
127.0.0.1:6379> set k3 v3           # 向192.168.81.100写入数据
OK
127.0.0.1:6379> info replication    # 查看复制信息
# Replication
role:master
connected_slaves:1
slave0:ip=192.168.81.101,port=6379,state=online,offset=8759,lag=1
master_repl_offset:8759             # 写入数据后192.168.81.100上的偏移量是8759
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:8758

查看192.168.81.101机器上Redis的偏移量

127.0.0.1:6379> info replication    # 查看复制信息
# Replication
role:slave
master_host:192.168.81.100
master_port:6379
master_link_status:up
master_last_io_seconds_ago:8
master_sync_in_progress:0
slave_repl_offset:8602
slave_priority:100
slave_read_only:1
connected_slaves:0
master_repl_offset:0                # 此时192.168.81.101上的偏移量是8602
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
127.0.0.1:6379> get k1
"v1"
127.0.0.1:6379> get k2
"v2"
127.0.0.1:6379> get k3
"v3"
127.0.0.1:6379> info replication    # 查看复制信息
# Replication
role:slave
master_host:192.168.81.100
master_port:6379
master_link_status:up
master_last_io_seconds_ago:7
master_sync_in_progress:0
slave_repl_offset:8759              # 同步数据后192.168.81.101上的偏移量是8759
slave_priority:100
slave_read_only:1
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0

如果主从节点上的offset差距太大,说明从节点没有从主节点同步数据,主从节点之间的连接出现问题:比如网络,阻塞,缓冲区等

(二)全量复制

如果一个主节点上已经写入很多数据,此时从节点不仅同步已经有的数据,同时同步slave在同步期间master上被写入的数据(如果在同步期间master被写入数据),以达到数据完全同步的目的,这就是Redis的全量复制的功能。

Redis的master会把当前的RDB文件同步给slave, 在此期间master中写入的数据会被写入复制缓冲区(repl_back_buffer)中,当RDB文件同步到slave完成,master通过偏移量的对比,把复制缓冲区(repl_back_buffer)中的数据同步给slave。

Redis使用psync命令进行数据全量复制和部分复制

psync命令有两个参数:run_id和偏移量

psync命令的步骤:

  1. 在slave第一次向master同步数据时,不知道master的run_id和offset,使用psync ? -1命令向master发起同步请求
  2. master接受请求后,知道slave是做全量复制,master就会把run_id和offset响应给slave
  3. slave保存master发送过来的run_id和offset
  4. master响应slave后,执行BGSAVE命令把当前所有数据生成RDB文件,然后将RDB文件同步给slave
  5. Redis中的repl_back_buffer复制缓冲区可以记录生成RDB文件之后到同步完成这个时间段时写入的数据,然后把这些数据也同步给slave
  6. slave执行flushall命令清空slave中原有的数据,然后从RDB文件读取所有的数据,保证slave与master中数据的同步

可以用下图表示:
在这里插入图片描述
全量复制的开销非常大

  • master执行BGSAVE命令会消耗一定时间
  • BGSAVE命令会fork子进程,对CPU,内存和硬盘都有一个消耗
  • master将RDB文件传输给slave的时间,传输过程中也会占用一定的网络带宽
  • slave清除原有数据的时间,如果slave中原有数据比较多,清空原有数据也会消耗一定的时间
  • slave加载RDB文件会消耗一定时间
  • 可能的AOF文件重写的时间:RDB文件加载完成,如果slave节点的AOF功能开启,则会执行AOF重写操作,保证AOF文件中保存最新的数据

除了上面提到的开销,如果master和slave之间的网络出现问题,则在一段时间内slave上同步的数据就会丢失。解决这个问题的最好办法就是再做一次全量复制,同步master中所有数据。Redis 2.8版本中添加了部分复制的功能,如果发生master和slave之间的网络出现问题时,使用部分复制尽可能的减少丢失数据的可能,而不用全部复制。

(三)部分复制

当master与slave之间的连接断开时,master在写入数据同时也会把写入的数据保存到repl_back_buffer复制缓冲区中。

当master与slave之间的网络连通后,slave会执行psync {offset} {run_id}命令,offset是slave节点上的偏移量。master接收到slave传输的偏移量,会与repl_back_buffer复制缓冲区中的offset做对比,如果接收到的offset小于repl_back_buffer中记录的偏移量,master就会把两个偏移量之间的数据发送给slave,slave同步完成,slave中的数据就与master中的数据一致。

可以用下图表示:
在这里插入图片描述
(四)主从复制故障

(1) slave宕机

处理方法是 将客户端交互的那个失效的salve 改成正常运行的salve。不过要考虑 这个正常的salve 能否承受这么大的压力
在这里插入图片描述

(2)master宕掉

Redis的master就无法提供服务了,只有slave可以提供数据读取服务

解决方法:把其中一个slave为成master,以提供写入数据功能,另外一台slave重新做为新的master的从节点,提供读取数据功能,这种解决方法依然需要手动完成。
在这里插入图片描述
在这里插入图片描述

主从模式没有实现故障的自动转移,这就引出了Redis的sentinel(哨兵)了。

(四)开发中可能遇到的问题

4 - 1读写分离问题

读写分离:master负责写入数据,把读取数据的流量分摊到slave节点。读写分离一方面可以减轻master的压力,另一方面又扩展了读取数据的能力
在这里插入图片描述
但是读写分离可能遇到以下问题:

(1).复制数据延迟

大多数情况下,master采用异步方式将数据同步给slave,在这个过程中会有一个时间差。当slave遇到阻塞时,接收数据会有一定延迟,在这个时间段内从slave读取数据可能会出现数据不一致的情况。

解决方法是:可以对master和slave的offset值进行监控,当offset值相差过多时,可以把读流量转换到master上,但是这种方式有一定的成本

(2).读到过期数据

Redis删除过期数据有两种策略

方式一:懒惰策略

当Redis操作这个数据时,才会去看这个数据是否过期,如果数据已经过期,会返回一个-2给客户端,表示查询的数据已经过期

方式二:定时任务

每隔一个周期,Redis会采集一部分key,看这些key是否过期
如果过期key非常多或者采样速度慢于key过期速度时,就会有很多过期key没有被删除
此时slave会同步包括过期key在内的master上的所有数据
由于slave没有删除数据的权限,此时基于读写分离的模式,客户端会从slave中读取一些过期的数据,也即脏数据

(3)从节点故障

上面已经介绍了。

4-2 主从配置不一致

第一种情况是:例如maxmemory不一致:丢失数据

如master节点分配的内存为4G,而slave节点分配的内存只有2G时,此时虽然可以进行正常的主从复制,但当slave从master同步的数据大于2G时,slave不会抛出异常,不过会触发slave节点的maxmemory-policy策略,对同步的数据进行一部分的淘汰,此时slave中的数据已经不完整了,造成丢失数据的情况。

另一种主从配置不一致的情况是:对master节点进行数据结构优化,但是没有对slave做同样的优化,会造成master和slave的内存不一致

4-3 规避全量复制

全量复制的开销是非常大的,能避免就避免。主从节点的maxmemory不要设置过大,则传输和加载RDB文件的速度会很快,开销相对会小一些,也可以在用户访问量比较低时进行全量复制

不过对于第一次为一个master配置一个slave时,slave中没有任何数据,进行全量复制不可避免。

4-4 节点run_id不匹配

当master重启时,master的run_id会发生变化。slave在同步数据时发现之前保存的master的run_id与现在的run_id不匹配,会认为当前master不安全。

解决方法:
做一次全量复制,当master发生故障时,slave转换为master提供数据写入,或者使用Redis哨兵和集群。
Redis4.0版本中提供新的方法:当master的run_id发生改变时,做故障转移可以避免做全量复制

4-5 复制缓冲区不足

复制缓冲区的作用是把新的命令写入到缓冲区中,复制缓冲区实际是一个队列,默认大小为1MB,即复制缓冲区只能保存1MB大小的数据。

如果slave与master的网络断开,master就会把新写入的数据保存到复制缓冲区中

当写入到复制缓冲区内的数据小于1MB时,就可以做部分复制,避免全量复制的问题。
如果新写入的数据大于1MB时,就只能做全量复制了。
在配置文件中修改rel_backlog_size选项来加大复制缓冲区的大小,来减少全量复制的情况出现。

4-6 规避复制风暴
主从架构中,master节点重启时,则master的run_id会发生变化,所有的slave节点都会进行主从复制

master生成RDB文件,然后所有slave节点都会同步RDB文件,在这个过程中对master节点的CPU,内存,硬盘有很大的开销,这就是复制风暴。

1、单主节点复制风暴解决方法:更换复制拓朴
在这里插入图片描述
2、单机多部署复制风暴
一台服务器上的所有节点都是master,如果这台服务器系统发生重启,则所有的slave节点都从这台服务器进行全量复制,会对服务器造成很大的压力

主节点分散多机器:将master分配到不同的服务器上
在这里插入图片描述

七、Redis sentinel

(一)主从复制高可用的问题

主从复制高可用的作用:

  1. 为master提供备份,当master宕机时,slave有完整的备份数据
  2. 对master实现分流,实现读写分离

但是主从架构有一个问题:
如果master宕机,故障转移需要手动完成或者由别的工具来完成,从slave中选择一个slave做为新的master

举个例子:在一主两从情况下,master宕机,则slave从master同步数据也断开,此时client向master写入数据会失败,读写分离时读取数据正常,但不能更新数据。

master出现故障之后,采用手动进行故障转移步骤

  1. 选择一个slave,执行slave no one命令使之成为一个master
  2. 对其余的slave执行slaveof new master命令,将这些slave指定为新的master的slave
  3. client会对新的master进行写入数据,从slave中读取原来的数据
    在这里插入图片描述
    上面的操作过程需要手动完成,或者编写脚本,由脚本来执行这个过程,但是这个过程是有问题的:
  • 怎么判断一个Redis节点是有问题的,怎么通知client对新master进行写入操作
  • 怎么保证一个完整的事务实现过程

这里就引出我们的主角-Redis Sentinel

(二)Redis Sentinel架构说明

Redis Sentinel的功能:对Redis节点进行监控,故障判断,故障转移,故障通知
在这里插入图片描述
对于Redis的主从架构,Redis Sentinel会运行几个sentinel进程,sentinel进程不操作数据,而是对Redis故障进行判断和转移。

多个sentinel运行,即使一个sentinel进程运行异常,还有别的sentinel继续运行,可以保证对故障节点判断的准确性,同时保证Redis的高可用。

对于redis-cli来说,Redis cli不会再记录Redis的IP和端口,而是从sentinel获取Redis信息,然后进行连接Redis节点,进行数据写入和读取操作,多个Redis Sentinel对所有的master和slave进行监控,会实时记录master和slave的地址信息。

在这里插入图片描述

Redis Sentinel故障转移步骤:

  1. 当某个master发生故障,多个sentinel会监控到这个异常,这些sentinel会按照一定规则从多个slave中选中一个做为新的master,并通知别的slave从新的master中同步数据
  2. 当某个slave转换为新的master,sentinel会记录新的master的地址信息和slave的地址信息,通知Redis cli
  3. Redis cli接收到新的master和slave的信息,就会向新的master写入数据,从slave中读取数据
  4. 等到原来的master重启之后,会变成新的master的slave,并从新的master同步数据

在这里插入图片描述
在上面的步骤里,sentinel实现了Redis的故障自动发现,自动转移和自动通知

说明:一套Redis sentinel集合可以通知master-name做为标识同时监控多套主从架构

(三)Redis Sentinel客户端连接解析

1.Redis Sentinel配置
(1)环境说明
例如:

192.168.81.100的6379端口为master节点
192.168.81.100的6380端口为slave节点
192.168.81.101的6379端口和6380端口为slave节点

在192.168.81.101的26379,26380,26381端口开启三个sentinel进行监控

(2)在192.168.81.100虚拟机上配置主从节点

[root@localhost ~]# cd /etc/                            # 进入/etc目录
[root@localhost ~]# systemctl stop redis                # 关闭系统中运行的redis
[root@localhost etc]# cp redis.conf redis_6379.conf     # 复制redis配置文件,以端口区分,方便后面进行配置
[root@localhost etc]# cp redis.conf redis_6380.conf     # 复制redis配置文件,以端口区分,方便后面进行配置
[root@localhost etc]# vi redis_6379.conf                # 编辑redis-server配置文件,修改下面几行
    bind 0.0.0.0                                        # 修改bing选项,才能从系统外连接redis
    protected-mode yes                                  # 开启保存模式
    port 6379                                           # 指定redis运行的端口
    daemonize yes                                       # 以守护进程启动redis
    pidfile "/var/run/redis_6379.pid"                   # 指定redis运行时pid保存路径
    logfile "/var/log/redis/redis_6379.log"             # 指定redis运行时日志保存路径
    dir /var/lib/redis_6379                             # 指定redis运行时数据文件保存路径
    
[root@localhost etc]# vi redis_6380.conf                # 修改redis-server,修改下面几行
    bind 0.0.0.0
    port 6380                                           # 指定redis运行的端口
    daemonize yes
    pidfile "/var/run/redis_6380.pid"
    logfile "/var/log/redis/redis_6380.log"
    dir /var/lib/redis_6380
    slaveof 192.168.81.100 6379                         # 指定redis-server为192.168.81.100:6379的slave
    
[root@localhost etc]# redis-server /etc/redis_6379.conf     # 指定配置文件运行redis-server
[root@localhost etc]# redis-server /etc/redis_6380.conf     # 指定配置文件运行redis-server
[root@localhost etc]# ps aux | grep redis-server            # 查看redis-server是否运行
root       2548  0.3  1.7 155192 17720 ?        Ssl  23:14   0:00 redis-server 0.0.0.0:6379
root       2562  1.3  1.7 155192 17596 ?        Ssl  23:15   0:00 redis-server 0.0.0.0:6380
root       2567  0.0  0.0 112648   960 pts/3    R+   23:15   0:00 grep --color=auto redis-server
[root@localhost etc]# redis-cli -p 6380 info replication    # 进入6380端口运行redis客户端,并执行'info replication'命令
# Replication
role:slave                              # 角色为slave
master_host:192.168.81.100              # master为192.168.81.100
master_port:6379
master_link_status:up
master_last_io_seconds_ago:1
master_sync_in_progress:0
slave_repl_offset:1919
slave_priority:100
slave_read_only:1
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0

(3)在192.168.81.101虚拟机上配置从节点

[root@mysql ~]# cd /etc/                                # 操作同192.168.81.100相同
[root@mysql ~]# systemctl stop redis
[root@mysql etc]# cp redis.conf redis_6379.conf
[root@mysql etc]# cp redis.conf redis_6380.conf
[root@mysql etc]# vi redis_6379.conf
    bind 0.0.0.0
    protected-mode yes
    port 6379
    daemonize yes
    pidfile "/var/run/redis_6379.pid"
    logfile "/var/log/redis/redis_6379.log"
    dir /var/lib/redis_6379
    slaveof 192.168.81.100 6379                         # 指定redis-server为192.168.81.100:6379的slave
    
[root@mysql etc]# vi redis_6380.conf    
    bind 0.0.0.0
    port 6380
    daemonize yes
    pidfile "/var/run/redis_6380.pid"
    logfile "/var/log/redis/redis_6380.log"
    dir /var/lib/redis_6380
    slaveof 192.168.81.100 6379                         # 指定redis-server为192.168.81.100:6379的slave
    
[root@mysql etc]# redis-server /etc/redis_6379.conf     # 指定配置文件运行redis-server
[root@mysql etc]# redis-server /etc/redis_6380.conf     # 指定配置文件运行redis-server
[root@mysql ~]# ps aux | grep redis-server              # 查看redis-server是否运行
root       2178  0.2  0.8 155204 17728 ?        Ssl  15:10   0:02 redis-server 0.0.0.0:6379
root       2184  0.2  0.8 155204 17724 ?        Ssl  15:10   0:02 redis-server 0.0.0.0:6380
root       2411  0.0  0.0 112664   972 pts/2    R+   15:29   0:00 grep --color=auto redis-server
[root@mysql ~]# redis-cli -p 6379 info replication
# Replication
role:slave                              # 角色为slave
master_host:192.168.81.100              # master为192.168.81.100
master_port:6379
master_link_status:up
master_last_io_seconds_ago:6
master_sync_in_progress:0
slave_repl_offset:1961
slave_priority:100
slave_read_only:1
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
[root@mysql ~]# redis-cli -p 6380 info replication   
# Replication
role:slave                              # 角色为slave
master_host:192.168.81.100              # master为192.168.81.100
master_port:6379
master_link_status:up
master_last_io_seconds_ago:2
master_sync_in_progress:0
slave_repl_offset:1975
slave_priority:100
slave_read_only:1
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0

(4)在192.168.81.101虚拟机上配置并运行sentinel

[root@mysql etc]# cp redis-sentinel.conf sentinel_26379.conf    # 复制sentinel配置文件,方便区分
[root@mysql etc]# cp redis-sentinel.conf sentinel_26380.conf    # 复制sentinel配置文件,方便区分
[root@mysql etc]# cp redis-sentinel.conf sentinel_26381.conf    # 复制sentinel配置文件,方便区分
[root@mysql etc]# vi sentinel_26379.conf                        # 修改sentinel配置文件,修改下面几行
    daemonize yes                                               # 以守护进程方式启动
    port 26379                                                  # 指定端口
    protected-mode no                                           # 关闭保护模式
    sentinel monitor mymaster 192.168.81.100 6379 2             # 设置sentinel监控信息
    logfile /var/log/redis/sentinel_26379.log                   # 设置日志文件保存路径
[root@mysql etc]# vi sentinel_26380.conf 
    daemonize yes
    port 26380  
    protected-mode no  
    sentinel monitor mymaster 192.168.81.100 6379 2
    logfile /var/log/redis/sentinel_26380.log
[root@mysql etc]# vi sentinel_26381.conf 
    protected-mode no  
    port 26381
    daemonize yes  
    sentinel monitor mymaster 192.168.81.100 6379 2
    logfile /var/log/redis/sentinel_26381.log
[root@mysql etc]# redis-sentinel /etc/sentinel_26379.conf       # 指定配置文件,启动Redis Sentinel
[root@mysql etc]# redis-sentinel /etc/sentinel_26380.conf       # 指定配置文件,启动Redis Sentinel  
[root@mysql etc]# redis-sentinel /etc/sentinel_26381.conf       # 指定配置文件,启动Redis Sentinel
[root@mysql etc]# ps aux | grep sentinel                        # 查看Redis Sentinel是否运行
root       2709  0.9  0.2 142916  5464 ?        Ssl  15:49   0:00 redis-sentinel *:26379 [sentinel]
root       2713  1.1  0.2 142916  5472 ?        Ssl  15:49   0:00 redis-sentinel *:26380 [sentinel]
root       2717  2.0  0.2 142916  5476 ?        Rsl  15:49   0:00 redis-sentinel *:26381 [sentinel]
root       2721  0.0  0.0 112664   964 pts/2    R+   15:49   0:00 grep --color=auto sentinel
[root@mysql ~]# redis-cli -p 26379
127.0.0.1:26379> ping                                           # 执行ping操作
PONG
127.0.0.1:26379> info sentinel                                  # 查看所有sentinel的信息
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=192.168.81.100:6379,slaves=3,sentinels=3                   # 被监控的Redis主从架构命名为mymaster,被监控Redis节点的master为192.168.81.100L6379,有三个slave,同时有3个sentinel运行
127.0.0.1:26379> exit
[root@mysql ~]# grep -v '^#' /etc/sentinel_26379.conf | grep -v '^$'     #查看sentinel_26379配置文件,去除注释和空行,Redis Sentinel向配置文件中添加了几行内容
port 26379                                                  # sentinel运行的端口
dir "/tmp"
sentinel myid 9611958fc3e8b7c2be43385e44be88f87d725a77
sentinel monitor mymaster 192.168.81.100 6379 2             # sentinel监控的Redis节点名为mymaster,master地址为192.168.81.100:6379,quorem设置为2
sentinel config-epoch mymaster 0
sentinel leader-epoch mymaster 0
logfile "/var/log/redis/sentinel_26379.log"
daemonize yes
sentinel known-slave mymaster 192.168.81.101 6379           # sentinel探测到的slave
sentinel known-slave mymaster 192.168.81.100 6380           # sentinel探测到的slave
sentinel known-slave mymaster 192.168.81.101 6380           # sentinel探测到的slave
sentinel known-sentinel mymaster 192.168.81.101 26380 17ca0cb82becb58bd24e5a87ee3b6e8e9a49caf1                    # Redis Sentinel深测到的别的运行的sentinel
sentinel known-sentinel mymaster 127.0.0.1 26381 fb9342f3007e2abff165f5c33de1d48cf089f062                    # Redis Sentinel深测到的别的运行的sentinel
sentinel current-epoch 0
[root@mysql ~]# grep -v '^#' /etc/sentinel_26380.conf | grep -v '^$'  
port 26380
dir "/tmp"
sentinel myid 17ca0cb82becb58bd24e5a87ee3b6e8e9a49caf1
sentinel monitor mymaster 192.168.81.100 6379 2
sentinel config-epoch mymaster 0
sentinel leader-epoch mymaster 0
logfile "/var/log/redis/sentinel_26380.log"
daemonize yes
sentinel known-slave mymaster 192.168.81.101 6379
sentinel known-slave mymaster 192.168.81.101 6380
sentinel known-slave mymaster 192.168.81.100 6380
sentinel known-sentinel mymaster 127.0.0.1 26381 fb9342f3007e2abff165f5c33de1d48cf089f062
sentinel known-sentinel mymaster 192.168.81.101 26379 9611958fc3e8b7c2be43385e44be88f87d725a77
sentinel current-epoch 0
[root@mysql ~]# grep -v '^#' /etc/sentinel_26381.conf | grep -v '^$' 
port 26381
daemonize yes
dir "/tmp"
sentinel myid fb9342f3007e2abff165f5c33de1d48cf089f062
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel config-epoch mymaster 0
sentinel leader-epoch mymaster 0
logfile "/var/log/redis/sentinel_26381.log"
sentinel known-sentinel mymaster 127.0.0.1 26380 17ca0cb82becb58bd24e5a87ee3b6e8e9a49caf1
sentinel known-sentinel mymaster 192.168.81.101 26379 9611958fc3e8b7c2be43385e44be88f87d725a77
sentinel current-epoch 0

至此,3个sentinel已经正常运行了

python客户端持续通过sentinel向Redis写入数据,读取数据

import random
import time
from redis.sentinel import Sentinel

sentinel = Sentinel([
    ('192.168.81.101',26379),
    ('192.168.81.101',26380),
    ('192.168.81.101',26381),
],socket_timeout=0.1)               # 传入Redis Sentinel集合

while True:
    try:
        master = sentinel.discover_master('mymaster')
        print('current master IP:',master)          # 打印当前master的IP地址和端口
        val = random.randint(0,10000)               # 获取10000以内随机整数
        key = 'k%d' % val
        m = sentinel.master_for('mymaster', socket_timeout=0.5)
        m.set(key,'val%d' % val)                    # 通过sentinel向master节点写入数据
        v = m.get(key)                              # 通过sentinel读取数据
        print('{0} value is {1}'.format(key,v))
        time.sleep(1)
    except Exception as e:
        print("get no val:",e)

Redis Sentinel的高可用指的是服务端的高可用,对于Redis服务端的master宕机,sentinel可以对故障实现自动发现,自动转移,自动通知。这个过程客户端是感知不到的

Redis高可用即依赖于服务端的高可用,又依赖于客户端的高可用

通过分析Redis Sentinel的请求响应流程,可以知道客户端实现高可用步骤:

  1. 客户端遍历sentinel节点集合,获取一个可用的sentinel节点,同时获取masterName
  2. 在可用的sentinel节点上执行sentinel的API,获取master的地址和端口
  3. 在sentinel内部,sentinel会按照一定频率在master或者slave上执行info命令,获取对应节点的信息
  4. 客户端获取到master的地址和端口,会执行role命令或者role replication命令,对master进行验证
  5. 当master出现故障,sentinel按照算法从slave中选出一个做为新的master,同时把其余的slave做为新的master的slave
  6. sentinel维护一个频道,sentinel在这个频道中发布消息,指出新master的IP和端口
  7. 客户端订阅这个频道,接收消息后就知道新master的IP和端口,向新master连接进行数据处理

在这里插入图片描述
python客户端接入Redis Sentinel需要两个参数:sentinel地址集合,masterName

需要注意的是Redis节点的配置文件中的protected-mode必须设置为yes,否则连接会失败

(四)Redis Sentinel实现原理

1.Redis Sentinel内部的三个定时任务

Redis Sentinel内部有三个定时任务来对redid节点进行故障判断和转移

  • 每10秒每个sentinel对master和slave执行info命令,以发现slave节点和确认主从关系

sentinel在master节点执行info replication命令,从命令执行结果中解析出slave节点

图为 第一个定时:每10秒info
在这里插入图片描述

  • 每2秒每个sentinel通过master节点的channel交换信息(发布订阅)

master节点上有一个发布订阅的channel频道:__sentinel__:hello,用于所有sentinel之间进行信息交换

一个sentinel发布消息,消息包含当前sentinel节点的信息,对其他sentinel节点的判断以及当前sentinel对master节点和slave节点的一些判断,其他sentinel都可以接收到这条消息。新加入sentinel节点时,sentinel节点之间可以相互感知,以达到信息交互的功能

图为: 第二个定时:每2秒发布订阅
在这里插入图片描述

  • 每1秒每个sentinel对其他sentinel节点和Redis节点执行ping操作

每个sentinel都可以知道其他sentinel节点,当监控的master发生故障时,方便进行判断和新master的挑选,这个定时任务是master进行故障判定的依据

图为: 第三个定时:每1秒ping
在这里插入图片描述

2.主观下线和客观下线

主观下线:每个sentinel节点对Redis节点失败的’偏见’

在redis-sentinel配置文件中,有下面这种配置

sentinel monitor <master-name> <ip> <redis-port> <quorum>
sentinel down-after-milliseconds <master-name> <timeout>

一个sentinel集合可以同时监控多个master,slave的节点,sentinel对多个master,slave节点进行区分的标识就是master-name,ip和port是master节点的IP地址和端口,quorum是master客观下线之后sentinel进行判断的节点数

sentinel对master进行主观下线判断的时间,单位为毫秒,每个sentinel每秒对master和slave执行ping操作,当sentinel对master或slave在timeout定义的毫秒时间内没有回复,则sentinel会认为这个节点已经被主观下线了。

在前面的例子中对sentinel的配置是

sentinel monitor mymaster 192.168.81.100 6379 2
sentinel down-after-milliseconds mymaster 30000

解释:

  • sentinel集合监控名为mymaster的master,slave节点
  • 被监控的master节点的IP地址是192.168.81.100,端口为6379,
  • sentinel会在__sentinel__:hello频道中交流对master节点的看法,如果sentinel节点都对master节点ping失败’达成共识’,sentinel个数超过quorum的个数,sentinel集合则会认为master节点客观下线
  • 当两个sentinel对master节点执行ping操作,在30000毫秒(30秒)时间内没有得到回复,则认为节点已经被主观下线
  • quorum建议设置为:(sentinel节点数 / 2) + 1,可以根据应用场景进行设定

3.sentinel领导者选举

只需要一个sentinel节点就可以完成故障转移,通过sentinel is-master-down-by-addr命令来完成sentinel交换对master节点的失败判定和新master的选举

完成sentinel领导者选举步骤:

  1. 每个做主观下线的sentinel节点向其他sentinel节点发送命令,要求将自己设置为领导者
  2. 收到命令的sentinel节点如果没有同意同意其他sentinel节点发送的命令,那么将同意该请求,否则拒绝
  3. 如果该sentinel节点发现自己的票数已经超过sentinel集合半数且超过quorum,将成为领导者
  4. 如果此过程中有多个sentinel节点成为领导者,那么将等待一段时间重新进行选举

4.故障转移(由sentinel领导者节点完成)

故障转移步骤:

  1. 从slave节点中选出一个合适的节点作为新的master节点
  2. 对选出的slave节点执行slaveof no one命令,使成为新的master节点
  3. 向剩余的slave节点发送命令,让slave节点成为新master节点的slave节点,然后从新master节点同步数据。数据同步规则和parallel-syncs参数有关,比如一个一主三从架构中,master故障,sentinel领导者从3个slave中选出一个作为新的master节点,剩余的两个slave节点会成为新master节点的slave,从新master节点同步同步数据,master节点只需要生成一次RDB文件。如果parallel-syncs参数设置为1,则剩余两个slave节点会按顺序从新master节点拷贝数据,一个slave切点拷贝完成,另外一个slave才会从新master节点拷贝数据;如果parallel-syncs参数设置为2,则两个slave节点会同时从master节点进行数据拷贝,这无疑会加入新master的开销。
  4. sentinel领导者会把原来的master节点设置为slave节点,并保持对其’关注’,当原来的master节点恢复后,sentinel会使其去复制新master节点的数据

5 slave节点的选择

slave节点选择规则

  1. 选择slave-priority(slave节点优先级)最高的slave节点,如果存在则返回,不存在则继续
  2. 选择复制偏移量(offset)最大的slave节点,offset最大说明对master的数据复制的最完整,如果存在则返回,不存在则继续
  3. 选择run_id最小的slave节点,run_id最小说明slave节点启动最早

6.Redis Sentine小总结:

  • Redis Sentinel是Redis的高可用实现方案:故障发现,故障自动转移,配置中心,客户端通知
  • Redis Sentinel是Redis 2.8版本开始才正式生产可用,之前版本不可用于生产
  • 尽可以在不同物理机上部署Redis Sentinel所有节点,但是最好一个局域网内
  • Redis Sentinel中sentinel节点个数应该大于等于3,且最好为奇数,可以保证判断的公平
  • Redis Sentinel中的数据节点与普通数据节点没有区别
  • 客户端初始化时连接的是Sentinel节点集合,不是具体的Redis节点,但是Sentinel只是配置中心不是代理
  • Redis Sentinel通过三个定时任务实现了Sentinel节点对于master,slave,其余sentinel节点的监控
  • Redis Sentinel在对节点做失败判定时分为主观下线和客观下线
  • 看懂Redis Sentinel故障转移日志对于Redis Sentinel以及问题排查非常有帮助
  • Redis Sentinel实现读写分离高可用可以依赖Redis Sentinel节点的消息通知,获取Redis数据节点的状态变化

八、Redis Cluster

RedisCluster 是 Redis 的亲⼉⼦,它是 Redis 作者⾃⼰提供的Redis 集群化⽅案。

(一)Redis原生命令搭建集群

搭建Redis Cluster主要步骤

  1. 配置开启节点
  2. meet
  3. 指派槽
  4. 主从关系分配

环境说明
两台虚拟机,IP地址分别为:192.168.81.100和192.168.81.101
两台虚拟机操作系统均为:CentOS 7.5 64位
两台虚拟机系统说明:

  • 使用yum方式安装Redis
  • 关闭firewalld防火墙

使用两台虚拟机的7000,7001,7002端口搭建三主三从的Redis Cluster

  • 其中192.168.81.100主机三个端口启动的Redis Server为主节点
  • 192.168.81.101主机三个端口启动的Redis Server为从节点

搭建Redis Cluster具体步骤
(1)在192.168.81.100虚拟机上操作

[root@host1 etc]# cd /opt
[root@host1 opt]# mkdir config
[root@host1 opt]# cd config
[root@host1 config]# vi redis_7000.conf             # 创建7000端口运行需要的配置文件,文件内容如下
    port 7000
    daemonize yes
    dir '/var/lib/redis'
    logfile '/var/log/redis/redis_7000.log'
    dbfilename 'redis_7000.data'
    cluster-enabled yes
    cluster-config-file nodes-7000.conf
    cluster-require-full-coverage no
[root@host1 config]# sed 's/7000/7001/g' redis_7000.conf > redis_7001.conf  # 把redis_7000.conf文件中的7000替换成7001,并生成redis_7001.conf文件
[root@host1 config]# sed 's/7000/7002/g' redis_7000.conf > redis_7002.conf  # 把redis_7000.conf文件中的7000替换成7002,并生成redis_7002.conf文件
[root@host1 config]# redis-server /opt/config/redis_7000.conf               # 指定配置文件,启动redis server节点
[root@host1 config]# redis-server /opt/config/redis_7001.conf               # 指定配置文件,启动redis server节点            
[root@host1 config]# redis-server /opt/config/redis_7002.conf               # 指定配置文件,启动redis server节点
[root@host1 config]# ps aux | grep redis-server                             # 查看redis-server进程运行情况                        
root       2444  0.1  0.5 142904  5328 ?        Ssl  19:38   0:00 redis-server *:7000 [cluster]
root       2475  0.0  0.5 142904  5328 ?        Ssl  19:39   0:00 redis-server *:7001 [cluster]
root       2479  0.2  0.5 142904  5328 ?        Ssl  19:39   0:00 redis-server *:7002 [cluster]
root       2483  0.0  0.0 112648   964 pts/0    R+   19:39   0:00 grep --color=auto redis-server

(2)在192.168.81.101虚拟机上进行第一步的操作,生成三个配置文件,并启动Redis server

[root@mysql config]# redis-server /opt/config/redis_7000.conf               # 指定配置文件,启动redis server节点
[root@mysql config]# redis-server /opt/config/redis_7001.conf               # 指定配置文件,启动redis server节点           
[root@mysql config]# redis-server /opt/config/redis_7002.conf               # 指定配置文件,启动redis server节点
[root@mysql config]# ps aux  | grep redis-server                            # 查看redis-server的进程运行情况                        
root       1704  0.1  0.2 142916  5344 ?        Ssl  19:41   0:00 redis-server *:7000 [cluster]
root       1736  0.0  0.2 142916  5344 ?        Ssl  19:41   0:00 redis-server *:7001 [cluster]
root       1740  0.0  0.2 142916  5340 ?        Ssl  19:42   0:00 redis-server *:7002 [cluster]

(3)在192.168.81.100虚拟机上查看cluster节点的信息

[root@host1 config]# redis-cli -p 7000
127.0.0.1:7000> set hello world             # 向当前节点执行写入命令,失败
(error) CLUSTERDOWN Hash slot not served
127.0.0.1:7000> cluster nodes               # 查看集群节点信息,只有当前节点的信息,且显示为主节点
d20aa403c5d7d9507adcc4ef6132c14f3c9486ed :7000 myself,master - 0 0 0 connected
127.0.0.1:7000> cluster info                # 查看cluster的信息
cluster_state:fail                          # 集群为失败状态
cluster_slots_assigned:0
cluster_slots_ok:0                          # 当前集群中有0个slot(槽)
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:1                       # 当前集群中节点个数为1
cluster_size:0
cluster_current_epoch:0
cluster_my_epoch:0
cluster_stats_messages_sent:0
cluster_stats_messages_received:0
127.0.0.1:7000> exit
[root@host1 config]# cd /var/lib/redis/
[root@host1 redis]# ls              
dump.rdb  nodes-7000.conf  nodes-7001.conf  nodes-7002.conf
[root@host1 redis]# cat nodes-7000.conf    # 查看7000端口上运行的redis-server的run_id与执行cluster nodes命令得到的run_id相同
d20aa403c5d7d9507adcc4ef6132c14f3c9486ed :0 myself,master - 0 0 0 connected
vars currentEpoch 0 lastVoteEpoch 0

(4)在192.168.81.100上执行meet操作

[root@host1 redis]# redis-cli -p 7000 cluster meet 127.0.0.1 7001       # 通过7000端口的redis server向7001端口的redis server发送meet操作
OK
[root@host1 redis]# redis-cli -p 7000 cluster nodes                     # 通过7000端口运行的redis server查看cluster的节点信息,已经添加7001端口运行的redis server,且都为master节点
d20aa403c5d7d9507adcc4ef6132c14f3c9486ed 127.0.0.1:7000 myself,master - 0 0 0 connected
45d79e730593df3216a4236a19c21ff601949449 127.0.0.1:7001 master - 0 1539719395866 1 connected
[root@host1 redis]# redis-cli -p 7001 cluster nodes                     # 通过7001端口运行的redis server查看cluster的节点信息,也可以看到7000端口运行的redis server
d20aa403c5d7d9507adcc4ef6132c14f3c9486ed 127.0.0.1:7000 master - 0 1539719408423 0 connected
45d79e730593df3216a4236a19c21ff601949449 127.0.0.1:7001 myself,master - 0 0 1 connected
[root@host1 redis]# redis-cli -p 7002 cluster nodes                     # 通过7002端口的redis server查看cluster的节点信息,7000端口和7001端口运行的redis server都不在其中,因为7002端口运行的redis server没有meet
e5ea887c98e79ef2b2205d6cc1d7ac5cfe936d9a :7002 myself,master - 0 0 0 connected
[root@host1 redis]# redis-cli -p 7000 cluster meet 127.0.0.1 7002       # 向7002端口运行的redis server发送meet操作
OK
[root@host1 redis]# redis-cli -p 7001 cluster nodes                     # 7002端口运行的redis server已经添加到集群中了            
e5ea887c98e79ef2b2205d6cc1d7ac5cfe936d9a 127.0.0.1:7002 master - 0 1539719438785 0 connected
d20aa403c5d7d9507adcc4ef6132c14f3c9486ed 127.0.0.1:7000 master - 0 1539719439689 2 connected
45d79e730593df3216a4236a19c21ff601949449 127.0.0.1:7001 myself,master - 0 0 1 connected
[root@host1 redis]# redis-cli -p 7002 cluster nodes              
45d79e730593df3216a4236a19c21ff601949449 127.0.0.1:7001 master - 0 1539719442308 1 connected
d20aa403c5d7d9507adcc4ef6132c14f3c9486ed 127.0.0.1:7000 master - 0 1539719441301 2 connected
e5ea887c98e79ef2b2205d6cc1d7ac5cfe936d9a 127.0.0.1:7002 myself,master - 0 0 0 connected
[root@host1 redis]# redis-cli -p 7000 cluster meet 192.168.81.101 7002  # 向192.168.81.101:7002端口运行的redis server执行meet操作              
OK
[root@host1 redis]# redis-cli -p 7000 cluster meet 192.168.81.101 7000  # 向192.168.81.101:7000端口运行的redis server执行meet操作
OK
[root@host1 redis]# redis-cli -p 7000 cluster meet 192.168.81.101 7001  # 向192.168.81.101:7001端口运行的redis server执行meet操作
OK
[root@host1 redis]# redis-cli -p 7000 cluster nodes
c96589b19b8ff5d9d286d470b8efff9a540726d8 192.168.81.101:7000 master - 0 1539719546060 5 connected
53911425f4479ce4f054b8b714f4700715f0dc64 192.168.81.101:7002 master - 0 1539719548113 3 connected
bb88f0996503bac3b222b9cc4ec25139ad34ed3a 192.168.81.101:7001 master - 0 1539719547106 4 connected
45d79e730593df3216a4236a19c21ff601949449 127.0.0.1:7001 master - 0 1539719544042 1 connected
d20aa403c5d7d9507adcc4ef6132c14f3c9486ed 127.0.0.1:7000 myself,master - 0 0 2 connected
e5ea887c98e79ef2b2205d6cc1d7ac5cfe936d9a 127.0.0.1:7002 master - 0 1539719545050 0 connected
[root@host1 redis]# redis-cli -p 7002 cluster nodes                     # 查看集群中的节点信息,发现6个node都已经添加到当前集群中了
45d79e730593df3216a4236a19c21ff601949449 127.0.0.1:7001 master - 0 1539719554166 1 connected
d20aa403c5d7d9507adcc4ef6132c14f3c9486ed 127.0.0.1:7000 master - 0 1539719554668 2 connected
bb88f0996503bac3b222b9cc4ec25139ad34ed3a 192.168.81.101:7001 master - 0 1539719555174 4 connected
53911425f4479ce4f054b8b714f4700715f0dc64 192.168.81.101:7002 master - 0 1539719556180 3 connected
e5ea887c98e79ef2b2205d6cc1d7ac5cfe936d9a 127.0.0.1:7002 myself,master - 0 0 0 connected
c96589b19b8ff5d9d286d470b8efff9a540726d8 192.168.81.101:7000 master - 0 1539719557188 5 connected
[root@host1 redis]# redis-cli -p 7002 cluster info         # 查看集群相关的信息
cluster_state:fail                  # 集群状态为失败
cluster_slots_assigned:0
cluster_slots_ok:0
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6               # 集群中有6个已知节点
cluster_size:0
cluster_current_epoch:5
cluster_my_epoch:0
cluster_stats_messages_sent:507
cluster_stats_messages_received:507

(5)在192.168.81.101虚拟机上查看cluster的信息

[root@mysql config]# redis-cli -p 7000 cluster nodes        # 查看cluster的节点信息,6个节点的信息都显示出来,且都为master
53911425f4479ce4f054b8b714f4700715f0dc64 192.168.81.101:7002 master - 0 1539690828453 3 connected
45d79e730593df3216a4236a19c21ff601949449 192.168.81.100:7001 master - 0 1539690825432 1 connected
d20aa403c5d7d9507adcc4ef6132c14f3c9486ed 192.168.81.100:7000 master - 0 1539690826437 2 connected
e5ea887c98e79ef2b2205d6cc1d7ac5cfe936d9a 192.168.81.100:7002 master - 0 1539690827448 0 connected
bb88f0996503bac3b222b9cc4ec25139ad34ed3a 192.168.81.101:7001 master - 0 1539690827950 4 connected
c96589b19b8ff5d9d286d470b8efff9a540726d8 192.168.81.101:7000 myself,master - 0 0 5 connected
[root@mysql config]# redis-cli -p 7001 cluster nodes        # 查看cluster的节点信息,6个节点的信息都显示出来,且都为master
53911425f4479ce4f054b8b714f4700715f0dc64 127.0.0.1:7002 master - 0 1539690840955 3 connected
c96589b19b8ff5d9d286d470b8efff9a540726d8 192.168.81.101:7000 master - 0 1539690842968 5 connected
bb88f0996503bac3b222b9cc4ec25139ad34ed3a 192.168.81.101:7001 myself,master - 0 0 4 connected
e5ea887c98e79ef2b2205d6cc1d7ac5cfe936d9a 192.168.81.100:7002 master - 0 1539690844979 0 connected
d20aa403c5d7d9507adcc4ef6132c14f3c9486ed 192.168.81.100:7000 master - 0 1539690841960 2 connected
45d79e730593df3216a4236a19c21ff601949449 192.168.81.100:7001 master - 0 1539690843977 1 connected
[root@mysql config]# redis-cli -p 7002 cluster nodes        # 查看cluster的节点信息,6个节点的信息都显示出来,且都为master
bb88f0996503bac3b222b9cc4ec25139ad34ed3a 127.0.0.1:7001 master - 0 1539690847798 4 connected
53911425f4479ce4f054b8b714f4700715f0dc64 192.168.81.101:7002 myself,master - 0 0 3 connected
d20aa403c5d7d9507adcc4ef6132c14f3c9486ed 192.168.81.100:7000 master - 0 1539690846795 2 connected
e5ea887c98e79ef2b2205d6cc1d7ac5cfe936d9a 192.168.81.100:7002 master - 0 1539690843775 0 connected
c96589b19b8ff5d9d286d470b8efff9a540726d8 192.168.81.101:7000 master - 0 1539690844780 5 connected
45d79e730593df3216a4236a19c21ff601949449 192.168.81.100:7001 master - 0 1539690845786 1 connected
[root@mysql config]# redis-cli -p 7002 cluster info         # 查看cluster的信息
cluster_state:fail              # 集群状态为失败状态
cluster_slots_assigned:0
cluster_slots_ok:0
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6           # 集群中添加了6个节点
cluster_size:0                  # 集群中
cluster_current_epoch:5
cluster_my_epoch:3
cluster_stats_messages_sent:413
cluster_stats_messages_received:413

(6)在192.168.81.100虚拟机上编写脚本,完成分配slot

[root@host1 ~]# cd /opt/config/
[root@host1 config]# vi add_slots.sh                    # 编写add_slots.sh脚本

    #!/bin/bash

    start=$1
    end=$2
    port=$3

    for slot in `seq ${start} ${end}`
    do
        echo "slot:${slot}"
        redis-cli -p ${port} cluster addslots ${slot}
    done                                                                                                                
[root@host1 config]# sh add_slots.sh 0 5461 7000        # 运行add_slots.sh脚本,把0到5461号槽分配给192.168.81.100:7000的redis server节点
slot:0
OK
slot:1
OK
slot:2
OK
slot:3
OK
slot:4
OK
slot:5
OK
...中间省略
slot:5459
OK
slot:5460
OK
slot:5461
OK
[root@host1 config]# redis-cli -p 7000 cluster info        # 查看cluster集群的信息
cluster_state:ok                    # 集群状态为OK状态
cluster_slots_assigned:5462         # 集群中已经有5462个数据槽
cluster_slots_ok:5462               # 5462个槽的状态为0K状态
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6               # 集群添加了6个节点   
cluster_size:1                      # 集群的大小为1个
cluster_current_epoch:5
cluster_my_epoch:2
cluster_stats_messages_sent:2237
cluster_stats_messages_received:2237
[root@host1 config]# redis-cli -p 7000 cluster nodes        # 查看集群中节点的信息
c96589b19b8ff5d9d286d470b8efff9a540726d8 192.168.81.101:7000 master - 0 1539720483326 5 connected
53911425f4479ce4f054b8b714f4700715f0dc64 192.168.81.101:7002 master - 0 1539720484336 3 connected
bb88f0996503bac3b222b9cc4ec25139ad34ed3a 192.168.81.101:7001 master - 0 1539720485346 4 connected
45d79e730593df3216a4236a19c21ff601949449 127.0.0.1:7001 master - 0 1539720482314 1 connected
d20aa403c5d7d9507adcc4ef6132c14f3c9486ed 127.0.0.1:7000 myself,master - 0 0 2 connected 0-5461                # 192.168.81.100:7000端口运行的redis server已经分配的槽为0到5461号槽,其他节点还没有分配槽
e5ea887c98e79ef2b2205d6cc1d7ac5cfe936d9a 127.0.0.1:7002 master - 0 1539720486351 0 connected
[root@host1 config]# redis-cli -p 7002 cluster nodes 
45d79e730593df3216a4236a19c21ff601949449 127.0.0.1:7001 master - 0 1539720507768 1 connected
d20aa403c5d7d9507adcc4ef6132c14f3c9486ed 127.0.0.1:7000 master - 0 1539720509782 2 connected 0-5461
bb88f0996503bac3b222b9cc4ec25139ad34ed3a 192.168.81.101:7001 master - 0 1539720510789 4 connected
53911425f4479ce4f054b8b714f4700715f0dc64 192.168.81.101:7002 master - 0 1539720508776 3 connected
e5ea887c98e79ef2b2205d6cc1d7ac5cfe936d9a 127.0.0.1:7002 myself,master - 0 0 0 connected
c96589b19b8ff5d9d286d470b8efff9a540726d8 192.168.81.101:7000 master - 0 1539720511799 5 connected
[root@host1 config]# sh add_slots.sh 5462 10922 7001    # 运行add_slots.sh脚本,把5462号到10922号槽分配给7001端口运行的redis server
[root@host1 config]# sh add_slots.sh 10923 16383 7002   # 运行add_slots.sh脚本,把10923号到16383号槽分配给7002端口运行的redis server          
[root@host1 config]# redis-cli -p 7000 cluster nodes    # 查看集群的节点信息
c96589b19b8ff5d9d286d470b8efff9a540726d8 192.168.81.101:7000 master - 0 1539720810075 5 connected
53911425f4479ce4f054b8b714f4700715f0dc64 192.168.81.101:7002 master - 0 1539720807558 3 connected
bb88f0996503bac3b222b9cc4ec25139ad34ed3a 192.168.81.101:7001 master - 0 1539720804033 4 connected
45d79e730593df3216a4236a19c21ff601949449 127.0.0.1:7001 master - 0 1539720809067 1 connected 5462-10922        # 7001端口运行的redis server的槽编号为5462号到10922号槽
d20aa403c5d7d9507adcc4ef6132c14f3c9486ed 127.0.0.1:7000 myself,master - 0 0 2 connected 0-5461                            # 7000端口运行的redis server的槽编号为0号到5461号槽
e5ea887c98e79ef2b2205d6cc1d7ac5cfe936d9a 127.0.0.1:7002 master - 0 1539720808061 0 connected 10923-16383       # 7002端口运行的redis server的槽编号为10922号到16383号槽
[root@host1 config]# redis-cli -p 7000 cluster info     # 查看集群的信息
cluster_state:ok                    # 集群状态为ok状态
cluster_slots_assigned:16384        # 集群已经被分配16384个槽
cluster_slots_ok:16384              # 集群中状态为ok的槽有16384个
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6               # 集群有6个节点
cluster_size:3                      # 集群大小为3个
cluster_current_epoch:5
cluster_my_epoch:2
cluster_stats_messages_sent:2950
cluster_stats_messages_received:2950
[root@host1 config]# redis-cli -p 7001 cluster info 
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:5
cluster_my_epoch:1
cluster_stats_messages_sent:3158
cluster_stats_messages_received:3158
[root@host1 config]# redis-cli -p 7002 cluster info 
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:5
cluster_my_epoch:0
cluster_stats_messages_sent:2828
cluster_stats_messages_received:2828
[root@host1 config]# redis-cli -p 7000
127.0.0.1:7000> set hello world     # 成功向集群中写入数据
OK
127.0.0.1:7000> config get cluster* # 获取集群的配置信息
1) "cluster-node-timeout"
2) "15000"
3) "cluster-migration-barrier"
4) "1"
5) "cluster-slave-validity-factor"
6) "10"
7) "cluster-require-full-coverage"
8) "no"
127.0.0.1:7000> exit

(7)在192.168.81.101虚拟机上操作

[root@mysql config]# redis-cli -p 7002 cluster nodes        # 查看集群的节点信息,都是master
bb88f0996503bac3b222b9cc4ec25139ad34ed3a 127.0.0.1:7001 master - 0 1539693824725 4 connected
53911425f4479ce4f054b8b714f4700715f0dc64 192.168.81.101:7002 myself,master - 0 0 3 connected
d20aa403c5d7d9507adcc4ef6132c14f3c9486ed 192.168.81.100:7000 master - 0 1539693826743 2 connected 0-5461
e5ea887c98e79ef2b2205d6cc1d7ac5cfe936d9a 192.168.81.100:7002 master - 0 1539693829260 0 connected 10923-16383
c96589b19b8ff5d9d286d470b8efff9a540726d8 192.168.81.101:7000 master - 0 1539693830266 5 connected
45d79e730593df3216a4236a19c21ff601949449 192.168.81.100:7001 master - 0 1539693828252 1 connected 5462-10922
[root@mysql config]# redis-cli -p 7000 cluster replicate d20aa403c5d7d9507adcc4ef6132c14f3c9486ed        # 使192.168.81.101:7000端口运行的redis server做为192.168.81.100:7000端口运行的redis server的master
OK
[root@mysql config]# redis-cli -p 7001 cluster replicate 45d79e730593df3216a4236a19c21ff601949449
OK
[root@mysql config]# redis-cli -p 7002 cluster replicate e5ea887c98e79ef2b2205d6cc1d7ac5cfe936d9a 
OK
[root@mysql config]# redis-cli -p 7002 cluster nodes        # 查看集群的节点信息
bb88f0996503bac3b222b9cc4ec25139ad34ed3a 127.0.0.1:7001 slave 45d79e730593df3216a4236a19c21ff601949449 0 1539693962338 4 connected
53911425f4479ce4f054b8b714f4700715f0dc64 192.168.81.101:7002 myself,slave e5ea887c98e79ef2b2205d6cc1d7ac5cfe936d9a 0 0 3 connected
d20aa403c5d7d9507adcc4ef6132c14f3c9486ed 192.168.81.100:7000 master - 0 1539693958297 2 connected 0-5461
e5ea887c98e79ef2b2205d6cc1d7ac5cfe936d9a 192.168.81.100:7002 master - 0 1539693960324 0 connected 10923-16383
c96589b19b8ff5d9d286d470b8efff9a540726d8 192.168.81.101:7000 slave d20aa403c5d7d9507adcc4ef6132c14f3c9486ed 0 1539693961329 5 connected
45d79e730593df3216a4236a19c21ff601949449 192.168.81.100:7001 master - 0 1539693963347 1 connected 5462-10922
[root@mysql config]# redis-cli -p 7002 cluster slots        # 查看集群的槽与节点的关系
1) 1) (integer) 0
   2) (integer) 5461
   3) 1) "192.168.81.100"
      2) (integer) 7000
      3) "d20aa403c5d7d9507adcc4ef6132c14f3c9486ed"
   4) 1) "192.168.81.101"
      2) (integer) 7000
      3) "c96589b19b8ff5d9d286d470b8efff9a540726d8"
2) 1) (integer) 10923
   2) (integer) 16383
   3) 1) "192.168.81.100"
      2) (integer) 7002
      3) "e5ea887c98e79ef2b2205d6cc1d7ac5cfe936d9a"
   4) 1) "192.168.81.101"
      2) (integer) 7002
      3) "53911425f4479ce4f054b8b714f4700715f0dc64"
3) 1) (integer) 5462
   2) (integer) 10922
   3) 1) "192.168.81.100"
      2) (integer) 7001
      3) "45d79e730593df3216a4236a19c21ff601949449"
   4) 1) "127.0.0.1"
      2) (integer) 7001
      3) "bb88f0996503bac3b222b9cc4ec25139ad34ed3a"

(8)向集群中写入数据

[root@host1 config]# redis-cli -c -p 7000
127.0.0.1:7000> set hello world
OK
127.0.0.1:7000> exit

基本的环境已经搭建完毕

下面看一下集群扩容
(1)在192.168.81.100虚拟机上生成配置文件,并启动redis server

[root@host1 config]# ls
add_slots.sh  redis_7000.conf  redis_7001.conf  redis_7002.conf
[root@host1 config]# sed 's/7000/7003/g' redis_7000.conf > redis_7003.conf  # 生成配置文件
[root@host1 config]# redis-server /opt/config/redis_7003.conf               # 启动redis-server
[root@host1 config]# ps aux | grep redis-server
root       2553  0.2  0.7 142904  7552 ?        Ssl  12:23   0:12 redis-server 0.0.0.0:7000 [cluster]
root       2557  0.2  0.7 142904  7548 ?        Ssl  12:23   0:13 redis-server 0.0.0.0:7001 [cluster]
root       2561  0.2  0.7 142904  7556 ?        Ssl  12:23   0:13 redis-server 0.0.0.0:7002 [cluster]
root       2596  0.1  0.5 142904  5336 ?        Ssl  13:53   0:00 redis-server 0.0.0.0:7003 [cluster]
root       2600  0.0  0.0 112648   964 pts/0    R+   13:53   0:00 grep --color=auto redis-server

(2)在192.168.81.101虚拟机上生成配置文件,并启动redis-server

[root@mysql config]# ls
dump.rdb  redis_7000.conf  redis_7001.conf  redis_7002.conf
[root@mysql config]# sed 's/7000/7003/g' redis_7000.conf > redis_7003.conf      # 生成配置文件
[root@mysql config]# ls
dump.rdb  redis_7000.conf  redis_7001.conf  redis_7002.conf  redis_7003.conf
[root@mysql config]# redis-server /opt/config/redis_7003.conf   # 指定配置文件,启动redis-server
[root@mysql config]# ps aux | grep redis-server
root       1948  0.2  0.3 142916  7560 ?        Ssl  12:21   0:13 redis-server 0.0.0.0:7002 [cluster]
root       1952  0.2  0.3 142916  7560 ?        Ssl  12:21   0:12 redis-server 0.0.0.0:7001 [cluster]
root       1964  0.2  0.3 142916  7356 ?        Ssl  12:21   0:12 redis-server 0.0.0.0:7000 [cluster]
root       7348  0.0  0.2 142916  5352 ?        Ssl  13:53   0:00 redis-server 0.0.0.0:7003 [cluster]
root       7352  0.0  0.0 112664   972 pts/0    R+   13:53   0:00 grep --color=auto redis-server

(3)在192.168.81.101虚拟机上执行meet操作,对集群扩容

[root@mysql config]# redis-cli -p 7000 cluster meet 127.0.0.1 7003          # 对新添加的redis-server执行meet操作
OK
[root@mysql config]# redis-cli -p 7000 cluster meet 192.168.81.100 7003
OK
[root@mysql config]# redis-cli -p 7000 cluster nodes            # 查看集群的节点信息
6f369311b0ca4c503f337c4bb23424eed3eeb188 127.0.0.1:7002 slave cb8c114d44d289687798508232d31e0a065fdab5 0 1539755696566 4 connected
ac153a3c1fb10d9d502b153c32dec65c85b2f97c 192.168.81.100:7002 slave fceba6001b95e2169ddd6622436b213324fe8f77 0 1539755699589 6 connected
71f5695dc1f4322c8ea9066d1b3cd8bb0ab7a329 192.168.81.100:7001 slave cf74b2d9570665b74525802462c74cf2e072ef99 0 1539755694553 5 connected
27266dcfd098dfe2a42361d6ab59edf8fb9f5413 192.168.81.100:7003 master - 0 1539755697570 7 connected       # 已经添加到集群中了
cb8c114d44d289687798508232d31e0a065fdab5 192.168.81.100:7000 master - 0 1539755694553 4 connected 5461-10922
fceba6001b95e2169ddd6622436b213324fe8f77 127.0.0.1:7001 master - 0 1539755698572 2 connected 10923-16383
cf74b2d9570665b74525802462c74cf2e072ef99 192.168.81.101:7000 myself,master - 0 0 1 connected 0-5460
2ff22acb1e006b9881abc80238e15b4e3fcefbef 127.0.0.1:7003 master - 0 1539755695560 0 connected       # 已经添加到集群中了
[root@mysql config]# redis-cli -p 7000 cluster info
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:8
cluster_size:3
cluster_current_epoch:7
cluster_my_epoch:1
cluster_stats_messages_sent:11829
cluster_stats_messages_received:11829
[root@mysql config]# redis-cli -p 7003 cluster replicate 27266dcfd098dfe2a42361d6ab59edf8fb9f5413        # 绑定主从关系
OK
[root@mysql config]# redis-cli -p 7003 cluster nodes 
27266dcfd098dfe2a42361d6ab59edf8fb9f5413 192.168.81.100:7003 master - 0 1539756040013 7 connected
2ff22acb1e006b9881abc80238e15b4e3fcefbef 192.168.81.101:7003 myself,slave 27266dcfd098dfe2a42361d6ab59edf8fb9f5413 0 0 0 connected
cb8c114d44d289687798508232d31e0a065fdab5 192.168.81.100:7000 master - 0 1539756035980 4 connected 5461-10922
ac153a3c1fb10d9d502b153c32dec65c85b2f97c 192.168.81.100:7002 slave fceba6001b95e2169ddd6622436b213324fe8f77 0 1539756037488 2 connected
cf74b2d9570665b74525802462c74cf2e072ef99 127.0.0.1:7000 master - 0 1539756040516 1 connected 0-5460
6f369311b0ca4c503f337c4bb23424eed3eeb188 127.0.0.1:7002 slave cb8c114d44d289687798508232d31e0a065fdab5 0 1539756038998 4 connected
fceba6001b95e2169ddd6622436b213324fe8f77 127.0.0.1:7001 master - 0 1539756041017 2 connected 10923-16383
71f5695dc1f4322c8ea9066d1b3cd8bb0ab7a329 192.168.81.100:7001 slave cf74b2d9570665b74525802462c74cf2e072ef99 1539756042024 1539756037992 1 connected

Redis原生命令搭建集群

优点

  • 可以深入理解Redis Cluster架构

缺点

  • 容易出差错,生产环境中不建议使用

(二)使用redis-trib.rb工具搭建集群

环境说明:
两台虚拟机,IP地址分别为:192.168.81.100和192.168.81.101
虚拟机系统为:CentOS 7.5
Redis为yum安装,版本为3.2
系统环境:关闭firewalld
Redis Cluster集群节点包括:192.168.81.101机器的9000.9001,9002端口运行的redis-server以及192.168.81.100机器的9000.9001,9002端口运行的redis-server

步骤:
(1)Ruby环境准备
官网下载Ruby源码包,解压,编译,安装,进行软链接

(2)Ruby环境安装redis模块

[root@mysql ruby_package]# ruby -v              # ruby安装完成,查看版本
ruby 2.3.7p456 (2018-03-28 revision 63024) [x86_64-linux]
[root@mysql ruby_package]# gem install redis    # 安装ruby操作redis模块
Successfully installed redis-4.0.2
Parsing documentation for redis-4.0.2
Done installing documentation for redis after 0 seconds
1 gem installed
[root@mysql ruby_package]# gem list -- check    # 查看ruby环境已经安装的模块

*** LOCAL GEMS ***

bigdecimal (1.2.8)
did_you_mean (1.0.0)
io-console (0.4.5)
json (1.8.3.1)
minitest (5.8.5)
net-telnet (0.1.1)
power_assert (0.2.6)
psych (2.1.0.1)
rake (10.4.2)
rdoc (4.2.1)
redis (4.0.2, 3.3.0)
test-unit (3.1.5)

(3)获取redis-trib.rb工具

[root@mysql ruby_package]# rz       # 从Redis官网下载Redis安装包,并上传到虚拟机中
rz waiting to receive.
Starting zmodem transfer.  Press Ctrl+C to cancel.
Transferring redis-3.2.12.tar.gz...
  100%    1515 KB    1515 KB/sec    00:00:01       0 Errors  

[root@mysql ruby_package]# ls
redis-3.2.12.tar.gz  ruby-2.3.7  ruby-2.3.7.tar.gz  rubygems-2.7.7.zip
[root@mysql ruby_package]# tar xf redis-3.2.12.tar.gz       # 对Redis安装包进行解压
[root@mysql ruby_package]# ls
[root@mysql ruby_package]# cd redis-3.2.12/src      # 进入Redis目录的src目录下
[root@mysql src]# ls
adlist.c     asciilogo.h  crc64.h       help.h         Makefile.dep     quicklist.h        release.c      setproctitle.c  t_hash.c      zipmap.c
adlist.h     bio.c        db.c          hyperloglog.c  memtest.c        rand.c             replication.c  sha1.c          t_list.c      zipmap.h
ae.c         bio.h        debug.c       intset.c       mkreleasehdr.sh  rand.h             rio.c          sha1.h          t_set.c       zmalloc.c
ae_epoll.c   bitops.c     debugmacro.h  intset.h       multi.c          rdb.c              rio.h          slowlog.c       t_string.c    zmalloc.h
ae_evport.c  blocked.c    dict.c        latency.c      networking.c     rdb.h              scripting.c    slowlog.h       t_zset.c
ae.h         cluster.c    dict.h        latency.h      notify.c         redisassert.h      sdsalloc.h     solarisfixes.h  util.c
ae_kqueue.c  cluster.h    endianconv.c  lzf_c.c        object.c         redis-benchmark.c  sds.c          sort.c          util.h
ae_select.c  config.c     endianconv.h  lzf_d.c        pqsort.c         redis-check-aof.c  sds.h          sparkline.c     valgrind.sup
anet.c       config.h     fmacros.h     lzf.h          pqsort.h         redis-check-rdb.c  sentinel.c     sparkline.h     version.h
anet.h       crc16.c      geo.c         lzfP.h         pubsub.c         redis-cli.c        server.c       syncio.c        ziplist.c
aof.c        crc64.c      geo.h         Makefile       quicklist.c      redis-trib.rb      server.h       testhelp.h      ziplist.h
[root@mysql src]# cp redis-trib.rb /usr/bin     # 把redis-trib.rb拷贝到/usr/bin目录下
[root@mysql src]# redis-trib.rb                 # 查看redis-trib.rb的帮助文档
Usage: redis-trib <command> <options> <arguments ...>

  create          mysql:port1 ... hostN:portN
                  --replicas <arg>
  check           host:port
  info            host:port
  fix             host:port
                  --timeout <arg>
  reshard         host:port
                  --from <arg>
                  --to <arg>
                  --slots <arg>
                  --yes
                  --timeout <arg>
                  --pipeline <arg>
  rebalance       host:port
                  --weight <arg>
                  --auto-weights
                  --use-empty-masters
                  --timeout <arg>
                  --simulate
                  --pipeline <arg>
                  --threshold <arg>
  add-node        new_host:new_port existing_host:existing_port
                  --slave
                  --master-id <arg>
  del-node        host:port node_id
  set-timeout     host:port milliseconds
  call            host:port command arg arg .. arg
  import          host:port
                  --from <arg>
                  --copy
                  --replace
  help            (show this help)

For check, fix, reshard, del-node, set-timeout you can specify the host and port of any working node in the cluster.

(4)在192.168.81.101虚拟机上创建配置文件,并非常用启动Redis server

[root@mysql ~]# ps aux | grep redis-server
root       1684  0.0  0.0 112664   972 pts/0    R+   11:44   0:00 grep --color=auto redis-server
[root@mysql ~]# cd /opt/config/
[root@mysql config]# ls
redis_7000.conf  redis_7001.conf  redis_7002.conf
[root@mysql config]# sed 's/7000/9000/g' redis_7000.conf > redis_9000.conf      # 创建redis_9000.conf配置文件
[root@mysql config]# sed 's/7000/9001/g' redis_7000.conf > redis_9001.conf      # 创建redis_9001.conf配置文件
[root@mysql config]# sed 's/7000/9002/g' redis_7000.conf > redis_9002.conf      # 创建redis_9002.conf配置文件
[root@mysql config]# redis-server /opt/config/redis_9000.conf       # 指定配置文件,启动redis-server
[root@mysql config]# redis-server /opt/config/redis_9001.conf       # 指定配置文件,启动redis-server
[root@mysql config]# redis-server /opt/config/redis_9002.conf       # 指定配置文件,启动redis-server
[root@mysql config]# ps aux | grep redis-server         # 查看已经启动的redis-server
root       1948  0.2  0.3 142916  7572 ?        Ssl  12:21   0:02 redis-server 0.0.0.0:9002 [cluster]
root       1952  0.2  0.3 142916  7560 ?        Ssl  12:21   0:02 redis-server 0.0.0.0:9001 [cluster]
root       1964  0.2  0.3 142916  7356 ?        Ssl  12:21   0:02 redis-server 0.0.0.0:9000 [cluster]
root       6640  0.0  0.0 112664   972 pts/1    R+   12:42   0:00 grep --color=auto redis-server
[root@mysql config]# cat redis_9000.conf        # redis_9000.conf配置文件内容
port 9000
bind 0.0.0.0
daemonize yes
dir '/var/lib/redis'
logfile '/var/log/redis/redis_9000.log'
dbfilename 'redis_9000.data'
cluster-enabled yes
cluster-config-file nodes-9000.conf
cluster-require-full-coverage no
[root@mysql config]# redis-cli -p 9000 cluster nodes        # 查看集群中节点的信息,都只显示自身节点
5eba129e4f4a4be2ffe630fe9fac19ba30f5b419 :9000 myself,master - 0 0 0 connected
[root@mysql config]# redis-cli -p 9001 cluster nodes        # 查看集群中节点的信息,都只显示自身节点
32d2d969c41f4af646b1052f10d69fd29510f3e4 :9001 myself,master - 0 0 0 connected
[root@mysql config]# redis-cli -p 9002 cluster nodes        # 查看集群中节点的信息,都只显示自身节点
6c43b4ddfeaeb3030af397e7469bb0d0b7673979 :9002 myself,master - 0 0 0 connected

使用同样的步骤,在192.168.81.100虚拟机上生成配置文件:redis_9000.conf,redis_9001.conf,redis_9002.conf

(5)在192.168.81.101虚拟机上使用redis-trib.rb工具构建集群

[root@mysql config]# redis-trib.rb create --replicas 1 127.0.0.1:9000 127.0.0.1:9001 127.0.0.1:9002 192.168.81.100:9000 192.168.81.100:9001 192.168.81.100:9002         # 构建集群,replicas后的参数为集群的节点
>>> Creating cluster
>>> Performing hash slots allocation on 6 nodes...
Using 3 masters:            # 指定master
127.0.0.1:9000
192.168.81.100:9000
127.0.0.1:9001
Adding replica 192.168.81.100:9001 to 127.0.0.1:9000        # 为master添加slave
Adding replica 127.0.0.1:9002 to 192.168.81.100:9000
Adding replica 192.168.81.100:9002 to 127.0.0.1:9001
M: cf74b2d9570665b74525802462c74cf2e072ef99 127.0.0.1:9000
   slots:0-5460 (5461 slots) master
M: fceba6001b95e2169ddd6622436b213324fe8f77 127.0.0.1:9001
   slots:10923-16383 (5461 slots) master
S: 6f369311b0ca4c503f337c4bb23424eed3eeb188 127.0.0.1:9002
   replicates cb8c114d44d289687798508232d31e0a065fdab5
M: cb8c114d44d289687798508232d31e0a065fdab5 192.168.81.100:9000
   slots:5461-10922 (5462 slots) master
S: 71f5695dc1f4322c8ea9066d1b3cd8bb0ab7a329 192.168.81.100:9001
   replicates cf74b2d9570665b74525802462c74cf2e072ef99
S: ac153a3c1fb10d9d502b153c32dec65c85b2f97c 192.168.81.100:9002
   replicates fceba6001b95e2169ddd6622436b213324fe8f77
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join..........
>>> Performing Cluster Check (using node 127.0.0.1:9000)
M: cf74b2d9570665b74525802462c74cf2e072ef99 127.0.0.1:9000
   slots:0-5460 (5461 slots) master
   1 additional replica(s)
M: cb8c114d44d289687798508232d31e0a065fdab5 192.168.81.100:9000
   slots:5461-10922 (5462 slots) master
   1 additional replica(s)
S: 6f369311b0ca4c503f337c4bb23424eed3eeb188 127.0.0.1:9002
   slots: (0 slots) slave
   replicates cb8c114d44d289687798508232d31e0a065fdab5
M: fceba6001b95e2169ddd6622436b213324fe8f77 127.0.0.1:9001
   slots:10923-16383 (5461 slots) master
   1 additional replica(s)
S: ac153a3c1fb10d9d502b153c32dec65c85b2f97c 192.168.81.100:9002
   slots: (0 slots) slave
   replicates fceba6001b95e2169ddd6622436b213324fe8f77
S: 71f5695dc1f4322c8ea9066d1b3cd8bb0ab7a329 192.168.81.100:9001
   slots: (0 slots) slave
   replicates cf74b2d9570665b74525802462c74cf2e072ef99
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

需要注意的是:如果上面的操作中,一直阻塞在’Waiting for the cluster to join’,则是因为meet操作阻塞,可以手动进行meet操作

具体步骤为:在当前虚拟机上另开启一个命令提示符,进行meet操作

[root@mysql ~]# redis-cli -p 9000 cluster meet 127.0.0.1 9001       # 192.168.81.101:9000端口运行的redis-server向192.168.81.101:9001端口运行的redis-server发送meet请求
OK
[root@mysql ~]# redis-cli -p 9000 cluster meet 127.0.0.1 9002       # 192.168.81.101:9000端口运行的redis-server向192.168.81.101:9001端口运行的redis-server发送meet请求
OK
[root@mysql ~]# redis-cli -p 9000 cluster meet 192.168.81.100 9002  # 192.168.81.101:9000端口运行的redis-server向192.168.81.100:9002端口运行的redis-server发送meet请求
OK
[root@mysql ~]# redis-cli -p 9000 cluster meet 192.168.81.100 9001  # 192.168.81.101:9000端口运行的redis-server向192.168.81.100:9001端口运行的redis-server发送meet请求
OK
[root@mysql ~]# redis-cli -p 9000 cluster meet 192.168.81.100 9000  # 192.168.81.101:9000端口运行的redis-server向192.168.81.100:9000端口运行的redis-server发送meet请求
OK
[root@mysql ~]# redis-cli -p 9000 cluster info      # 查看集群的信息
cluster_state:ok                        # 状态为ok
cluster_slots_assigned:16384            # 标记的slot共有16384个
cluster_slots_ok:16384                  # 已分配的slot有16384个
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6                   # 当前集群共有6个节点
cluster_size:3
cluster_current_epoch:6
cluster_my_epoch:1
cluster_stats_messages_sent:1890
cluster_stats_messages_received:1890
[root@mysql ~]# redis-cli -p 9000 cluster nodes         # 查看当前集群节点信息
cb8c114d44d289687798508232d31e0a065fdab5 192.168.81.100:9000 master - 0 1539751180759 4 connected 5461-10922
6f369311b0ca4c503f337c4bb23424eed3eeb188 127.0.0.1:9002 slave cb8c114d44d289687798508232d31e0a065fdab5 0 1539751183277 4 connected
fceba6001b95e2169ddd6622436b213324fe8f77 127.0.0.1:9001 master - 0 1539751181266 2 connected 10923-16383
ac153a3c1fb10d9d502b153c32dec65c85b2f97c 192.168.81.100:9002 slave fceba6001b95e2169ddd6622436b213324fe8f77 0 1539751182272 6 connected
71f5695dc1f4322c8ea9066d1b3cd8bb0ab7a329 192.168.81.100:9001 slave cf74b2d9570665b74525802462c74cf2e072ef99 0 1539751180255 5 connected
cf74b2d9570665b74525802462c74cf2e072ef99 127.0.0.1:9000 myself,master - 0 0 1 connected 0-5460      

基本环境搭建完成。


下面看看Redis Cluster扩容集群
(1)在192.168.81.100虚拟机上准备新节点

[root@host1 config]# ls
add_slots.sh  redis_9000.conf  redis_9001.conf  redis_9002.conf
[root@host1 config]# sed 's/9000/9003/g' redis_9000.conf > redis_9003.conf  # 生成配置文件
[root@host1 config]# redis-server /opt/config/redis_9003.conf               # 指定配置文件启动redis-server
[root@host1 config]# ps aux | grep redis-server                             # 查看redis-server的进程
root       2553  0.2  0.7 142904  7552 ?        Ssl  12:23   0:12 redis-server 0.0.0.0:9000 [cluster]
root       2557  0.2  0.7 142904  7548 ?        Ssl  12:23   0:13 redis-server 0.0.0.0:9001 [cluster]
root       2561  0.2  0.7 142904  7556 ?        Ssl  12:23   0:13 redis-server 0.0.0.0:9002 [cluster]
root       2596  0.1  0.5 142904  5336 ?        Ssl  13:53   0:00 redis-server 0.0.0.0:9003 [cluster]
root       2600  0.0  0.0 112648   964 pts/0    R+   13:53   0:00 grep --color=auto redis-server

(2)在192.168.81.101虚拟机上生成配置文件,手动扩容集群

[root@mysql config]# redis-cli -p 9000 cluster meet 127.0.0.1 9003          # 对新添加节点执行meet操作
OK
[root@mysql config]# redis-cli -p 9000 cluster meet 192.168.81.100 9003     # 对新添加节点执行meet操作
OK
[root@mysql config]# redis-cli -p 9000 cluster nodes            # 查看集群节点信息
6f369311b0ca4c503f337c4bb23424eed3eeb188 127.0.0.1:9002 slave cb8c114d44d289687798508232d31e0a065fdab5 0 1539755696566 4 connected
ac153a3c1fb10d9d502b153c32dec65c85b2f97c 192.168.81.100:9002 slave fceba6001b95e2169ddd6622436b213324fe8f77 0 1539755699589 6 connected
71f5695dc1f4322c8ea9066d1b3cd8bb0ab7a329 192.168.81.100:9001 slave cf74b2d9570665b74525802462c74cf2e072ef99 0 1539755694553 5 connected
27266dcfd098dfe2a42361d6ab59edf8fb9f5413 192.168.81.100:9003 master - 0 1539755697570 7 connected
cb8c114d44d289687798508232d31e0a065fdab5 192.168.81.100:9000 master - 0 1539755694553 4 connected 5461-10922
fceba6001b95e2169ddd6622436b213324fe8f77 127.0.0.1:9001 master - 0 1539755698572 2 connected 10923-16383
cf74b2d9570665b74525802462c74cf2e072ef99 192.168.81.101:9000 myself,master - 0 0 1 connected 0-5460
2ff22acb1e006b9881abc80238e15b4e3fcefbef 127.0.0.1:9003 master - 0 1539755695560 0 connected
[root@mysql config]# redis-cli -p 9000 cluster info         # 查看集群信息
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:8
cluster_size:3
cluster_current_epoch:7
cluster_my_epoch:1
cluster_stats_messages_sent:11829
cluster_stats_messages_received:11829
[root@mysql config]# redis-cli -p 9003 cluster replicate 27266dcfd098dfe2a42361d6ab59edf8fb9f5413            # 新添加节点设置主从关系 
OK
[root@mysql config]# redis-cli -p 9003 cluster nodes    # 查看集群中节点信息
27266dcfd098dfe2a42361d6ab59edf8fb9f5413 192.168.81.100:9003 master - 0 1539756040013 7 connected
2ff22acb1e006b9881abc80238e15b4e3fcefbef 192.168.81.101:9003 myself,slave 27266dcfd098dfe2a42361d6ab59edf8fb9f5413 0 0 0 connected
cb8c114d44d289687798508232d31e0a065fdab5 192.168.81.100:9000 master - 0 1539756035980 4 connected 5461-10922
ac153a3c1fb10d9d502b153c32dec65c85b2f97c 192.168.81.100:9002 slave fceba6001b95e2169ddd6622436b213324fe8f77 0 1539756037488 2 connected
cf74b2d9570665b74525802462c74cf2e072ef99 127.0.0.1:9000 master - 0 1539756040516 1 connected 0-5460
6f369311b0ca4c503f337c4bb23424eed3eeb188 127.0.0.1:9002 slave cb8c114d44d289687798508232d31e0a065fdab5 0 1539756038998 4 connected
fceba6001b95e2169ddd6622436b213324fe8f77 127.0.0.1:9001 master - 0 1539756041017 2 connected 10923-16383
71f5695dc1f4322c8ea9066d1b3cd8bb0ab7a329 192.168.81.100:9001 slave cf74b2d9570665b74525802462c74cf2e072ef99 1539756042024 1539756037992 1 connected

(3)在192.168.81.101虚拟机上生成配置文件,使用redis-trib.rb工具扩容集群
说明:
向集群中添加192.168.81.100:9003和192.168.81.101:9003节点
建议使用redis-trib.rb工具,避免新节点已经加入了其他集群,造成故障

[root@mysql config]# ls
dump.rdb  redis_9000.conf  redis_9001.conf  redis_9002.conf
[root@mysql config]# sed 's/9000/9003/g' redis_9000.conf > redis_9003.conf      # 生成配置文件
[root@mysql config]# ls
dump.rdb  redis_9000.conf  redis_9001.conf  redis_9002.conf  redis_9003.conf
[root@mysql config]# redis-server /opt/config/redis_9003.conf       # 指定配置文件运行redis-server
[root@mysql config]# ps aux | grep redis-server                     # 查看redis-server的进程信息
root       1948  0.2  0.3 142916  7560 ?        Ssl  12:21   0:13 redis-server 0.0.0.0:9002 [cluster]
root       1952  0.2  0.3 142916  7560 ?        Ssl  12:21   0:12 redis-server 0.0.0.0:9001 [cluster]
root       1964  0.2  0.3 142916  7356 ?        Ssl  12:21   0:12 redis-server 0.0.0.0:9000 [cluster]
root       7348  0.0  0.2 142916  5352 ?        Ssl  13:53   0:00 redis-server 0.0.0.0:9003 [cluster]
root       7352  0.0  0.0 112664   972 pts/0    R+   13:53   0:00 grep --color=auto redis-server
[root@mysql config]# redis-trib.rb add-node 127.0.0.1:9003 192.168.81.100:9003     # 向集群添加节点
[root@mysql config]# redis-cli -p 9000 cluster nodes        # 查看集群的节点信息
2ff22acb1e006b9881abc80238e15b4e3fcefbef 127.0.0.1:9003 slave 27266dcfd098dfe2a42361d6ab59edf8fb9f5413 0 1539826328082 7 connected
6f369311b0ca4c503f337c4bb23424eed3eeb188 127.0.0.1:9002 master - 0 1539826326067 9 connected 5461-10922
fceba6001b95e2169ddd6622436b213324fe8f77 127.0.0.1:9001 master - 0 1539826330601 2 connected 10923-16383
ac153a3c1fb10d9d502b153c32dec65c85b2f97c 192.168.81.100:9002 slave fceba6001b95e2169ddd6622436b213324fe8f77 0 1539826331108 6 connected
cb8c114d44d289687798508232d31e0a065fdab5 192.168.81.100:9000 slave 6f369311b0ca4c503f337c4bb23424eed3eeb188 0 1539826332115 9 connected
27266dcfd098dfe2a42361d6ab59edf8fb9f5413 192.168.81.100:9003 master - 0 1539826329091 7 connected
cf74b2d9570665b74525802462c74cf2e072ef99 192.168.81.101:9000 myself,master - 0 0 1 connected 0-5460
71f5695dc1f4322c8ea9066d1b3cd8bb0ab7a329 192.168.81.100:9001 slave cf74b2d9570665b74525802462c74cf2e072ef99 0 1539826330098 5 connected
[root@mysql config]# redis-trib.rb reshard 127.0.0.1:9000       # 迁移槽
>>> Performing Cluster Check (using node 127.0.0.1:9000)
M: cf74b2d9570665b74525802462c74cf2e072ef99 127.0.0.1:9000
   slots:0-5460 (5461 slots) master
   1 additional replica(s)
S: 2ff22acb1e006b9881abc80238e15b4e3fcefbef 127.0.0.1:9003
   slots: (0 slots) slave
   replicates 27266dcfd098dfe2a42361d6ab59edf8fb9f5413
M: 6f369311b0ca4c503f337c4bb23424eed3eeb188 127.0.0.1:9002
   slots:5461-10922 (5462 slots) master
   1 additional replica(s)
M: fceba6001b95e2169ddd6622436b213324fe8f77 127.0.0.1:9001
   slots:10923-16383 (5461 slots) master
   1 additional replica(s)
S: ac153a3c1fb10d9d502b153c32dec65c85b2f97c 192.168.81.100:9002
   slots: (0 slots) slave
   replicates fceba6001b95e2169ddd6622436b213324fe8f77
S: cb8c114d44d289687798508232d31e0a065fdab5 192.168.81.100:9000
   slots: (0 slots) slave
   replicates 6f369311b0ca4c503f337c4bb23424eed3eeb188
M: 27266dcfd098dfe2a42361d6ab59edf8fb9f5413 192.168.81.100:9003
   slots: (0 slots) master
   1 additional replica(s)
S: 71f5695dc1f4322c8ea9066d1b3cd8bb0ab7a329 192.168.81.100:9001
   slots: (0 slots) slave
   replicates cf74b2d9570665b74525802462c74cf2e072ef99
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
How many slots do you want to move (from 1 to 16384)? 4096
What is the receiving node ID? 27266dcfd098dfe2a42361d6ab59edf8fb9f5413
Please enter all the source node IDs.
  Type 'all' to use all the nodes as source nodes for the hash slots.
  Type 'done' once you entered all the source nodes IDs.
Source node #1:all

Ready to move 4096 slots.
  Source nodes:
    M: cf74b2d9570665b74525802462c74cf2e072ef99 127.0.0.1:9000
   slots:0-5460 (5461 slots) master
   1 additional replica(s)
    M: 6f369311b0ca4c503f337c4bb23424eed3eeb188 127.0.0.1:9002
   slots:5461-10922 (5462 slots) master
   1 additional replica(s)
    M: fceba6001b95e2169ddd6622436b213324fe8f77 127.0.0.1:9001
   slots:10923-16383 (5461 slots) master
   1 additional replica(s)
  Destination node:
    M: 27266dcfd098dfe2a42361d6ab59edf8fb9f5413 192.168.81.100:9003
   slots: (0 slots) master
   1 additional replica(s)
  Resharding plan:
    Moving slot 5461 from 6f369311b0ca4c503f337c4bb23424eed3eeb188
    Moving slot 5462 from 6f369311b0ca4c503f337c4bb23424eed3eeb188
    Moving slot 5463 from 6f369311b0ca4c503f337c4bb23424eed3eeb188
    Moving slot 5464 from 6f369311b0ca4c503f337c4bb23424eed3eeb188
    Moving slot 5465 from 6f369311b0ca4c503f337c4bb23424eed3eeb188
    Moving slot 5466 from 6f369311b0ca4c503f337c4bb23424eed3eeb188
    Moving slot 5467 from 6f369311b0ca4c503f337c4bb23424eed3eeb188
    Moving slot 5468 from 6f369311b0ca4c503f337c4bb23424eed3eeb188
    Moving slot 5469 from 6f369311b0ca4c503f337c4bb23424eed3eeb188
    Moving slot 5470 from 6f369311b0ca4c503f337c4bb23424eed3eeb188
    Moving slot 5471 from 6f369311b0ca4c503f337c4bb23424eed3eeb188
    Moving slot 5472 from 6f369311b0ca4c503f337c4bb23424eed3eeb188
    Moving slot 5473 from 6f369311b0ca4c503f337c4bb23424eed3eeb188
    Moving slot 5474 from 6f369311b0ca4c503f337c4bb23424eed3eeb188
    Moving slot 5475 from 6f369311b0ca4c503f337c4bb23424eed3eeb188
    Moving slot 5476 from 6f369311b0ca4c503f337c4bb23424eed3eeb188
    Moving slot 5477 from 6f369311b0ca4c503f337c4bb23424eed3eeb188
    Moving slot 5478 from 6f369311b0ca4c503f337c4bb23424eed3eeb188
    Moving slot 5479 from 6f369311b0ca4c503f337c4bb23424eed3eeb188
    Moving slot 5480 from 6f369311b0ca4c503f337c4bb23424eed3eeb188
    Moving slot 5481 from 6f369311b0ca4c503f337c4bb23424eed3eeb188
    Moving slot 5482 from 6f369311b0ca4c503f337c4bb23424eed3eeb188
    Moving slot 5483 from 6f369311b0ca4c503f337c4bb23424eed3eeb188
    Moving slot 5484 from 6f369311b0ca4c503f337c4bb23424eed3eeb188
    Moving slot 5485 from 6f369311b0ca4c503f337c4bb23424eed3eeb188
    Moving slot 5486 from 6f369311b0ca4c503f337c4bb23424eed3eeb188
    Moving slot 5487 from 6f369311b0ca4c503f337c4bb23424eed3eeb188
    Moving slot 5488 from 6f369311b0ca4c503f337c4bb23424eed3eeb188
    Moving slot 5489 from 6f369311b0ca4c503f337c4bb23424eed3eeb188
    Moving slot 5490 from 6f369311b0ca4c503f337c4bb23424eed3eeb188

Moving slot 12283 from 127.0.0.1:9001 to 192.168.81.100:9003: 
Moving slot 12284 from 127.0.0.1:9001 to 192.168.81.100:9003: 
Moving slot 12285 from 127.0.0.1:9001 to 192.168.81.100:9003: 
Moving slot 12286 from 127.0.0.1:9001 to 192.168.81.100:9003: 
Moving slot 12287 from 127.0.0.1:9001 to 192.168.81.100:9003: 
[root@mysql config]# redis-cli -p 9000 cluster slots            # 查看集群中槽的信息
1) 1) (integer) 6827
   2) (integer) 10922
   3) 1) "127.0.0.1"
      2) (integer) 9002
      3) "6f369311b0ca4c503f337c4bb23424eed3eeb188"
   4) 1) "192.168.81.100"
      2) (integer) 9000
      3) "cb8c114d44d289687798508232d31e0a065fdab5"
2) 1) (integer) 12288
   2) (integer) 16383
   3) 1) "127.0.0.1"
      2) (integer) 9001
      3) "fceba6001b95e2169ddd6622436b213324fe8f77"
   4) 1) "192.168.81.100"
      2) (integer) 9002
      3) "ac153a3c1fb10d9d502b153c32dec65c85b2f97c"
3) 1) (integer) 0
   2) (integer) 1364
   3) 1) "192.168.81.100"
      2) (integer) 9003
      3) "27266dcfd098dfe2a42361d6ab59edf8fb9f5413"
   4) 1) "127.0.0.1"
      2) (integer) 9003
      3) "2ff22acb1e006b9881abc80238e15b4e3fcefbef"
4) 1) (integer) 5461
   2) (integer) 6826
   3) 1) "192.168.81.100"
      2) (integer) 9003
      3) "27266dcfd098dfe2a42361d6ab59edf8fb9f5413"
   4) 1) "127.0.0.1"
      2) (integer) 9003
      3) "2ff22acb1e006b9881abc80238e15b4e3fcefbef"
5) 1) (integer) 10923
   2) (integer) 12287
   3) 1) "192.168.81.100"
      2) (integer) 9003
      3) "27266dcfd098dfe2a42361d6ab59edf8fb9f5413"
   4) 1) "127.0.0.1"
      2) (integer) 9003
      3) "2ff22acb1e006b9881abc80238e15b4e3fcefbef"
6) 1) (integer) 1365
   2) (integer) 5460
   3) 1) "192.168.81.101"
      2) (integer) 9000
      3) "cf74b2d9570665b74525802462c74cf2e072ef99"
   4) 1) "192.168.81.100"
      2) (integer) 9001
      3) "71f5695dc1f4322c8ea9066d1b3cd8bb0ab7a329"
[root@mysql config]# redis-cli -p 9000 cluster nodes            # 查看集群中节点的信息
2ff22acb1e006b9881abc80238e15b4e3fcefbef 127.0.0.1:9003 slave 27266dcfd098dfe2a42361d6ab59edf8fb9f5413 0 1539826689921 12 connected
6f369311b0ca4c503f337c4bb23424eed3eeb188 127.0.0.1:9002 master - 0 1539826692961 9 connected 6827-10922
fceba6001b95e2169ddd6622436b213324fe8f77 127.0.0.1:9001 master - 0 1539826693970 2 connected 12288-16383
ac153a3c1fb10d9d502b153c32dec65c85b2f97c 192.168.81.100:9002 slave fceba6001b95e2169ddd6622436b213324fe8f77 0 1539826688916 6 connected
cb8c114d44d289687798508232d31e0a065fdab5 192.168.81.100:9000 slave 6f369311b0ca4c503f337c4bb23424eed3eeb188 0 1539826693475 9 connected
27266dcfd098dfe2a42361d6ab59edf8fb9f5413 192.168.81.100:9003 master - 0 1539826690944 12 connected 0-1364 5461-6826 10923-12287
cf74b2d9570665b74525802462c74cf2e072ef99 192.168.81.101:9000 myself,master - 0 0 1 connected 1365-5460
71f5695dc1f4322c8ea9066d1b3cd8bb0ab7a329 192.168.81.100:9001 slave cf74b2d9570665b74525802462c74cf2e072ef99 0 1539826691953 5 connected
[root@mysql config]# redis-cli -p 9000 cluster nodes | grep master      # 只查看master的节点信息
6f369311b0ca4c503f337c4bb23424eed3eeb188 127.0.0.1:9002 master - 0 1539826726695 9 connected 6827-10922
fceba6001b95e2169ddd6622436b213324fe8f77 127.0.0.1:9001 master - 0 1539826728205 2 connected 12288-16383
27266dcfd098dfe2a42361d6ab59edf8fb9f5413 192.168.81.100:9003 master - 0 1539826726192 12 connected 0-1364 5461-6826 10923-12287
cf74b2d9570665b74525802462c74cf2e072ef99 192.168.81.101:9000 myself,master - 0 0 1 connected 1365-5460      

下面再看看.Redis Cluster集群收缩
说明:把192.168.81.101:9000这个master及对应slave节点从集群中移除

[root@mysql ~]# redis-cli -p 9000 cluster nodes         # 查看集群中的节点信息
2ff22acb1e006b9881abc80238e15b4e3fcefbef 127.0.0.1:9003 slave 27266dcfd098dfe2a42361d6ab59edf8fb9f5413 0 1539827599055 12 connected
6f369311b0ca4c503f337c4bb23424eed3eeb188 127.0.0.1:9002 master - 0 1539827597043 9 connected 6827-10922
fceba6001b95e2169ddd6622436b213324fe8f77 127.0.0.1:9001 master - 0 1539827598049 2 connected 12288-16383
ac153a3c1fb10d9d502b153c32dec65c85b2f97c 192.168.81.100:9002 slave fceba6001b95e2169ddd6622436b213324fe8f77 0 1539827601066 6 connected
cb8c114d44d289687798508232d31e0a065fdab5 192.168.81.100:9000 slave 6f369311b0ca4c503f337c4bb23424eed3eeb188 0 1539827596039 9 connected
27266dcfd098dfe2a42361d6ab59edf8fb9f5413 192.168.81.100:9003 master - 0 1539827600062 12 connected 0-1364 5461-6826 10923-12287
cf74b2d9570665b74525802462c74cf2e072ef99 192.168.81.101:9000 myself,master - 0 0 1 connected 1365-5460         # 要移除的节点的槽编号为:1365到5460
71f5695dc1f4322c8ea9066d1b3cd8bb0ab7a329 192.168.81.100:9001 slave cf74b2d9570665b74525802462c74cf2e072ef99 0 1539827595029 5 connected
[root@mysql ~]# redis-trib.rb reshard --from cf74b2d9570665b74525802462c74cf2e072ef99 --to 27266dcfd098dfe2a42361d6ab59edf8fb9f5413 --slots 1366 127.0.0.1:9001    # 迁移槽,从192.168.81.101:9000迁移到集群中别的节点上
>>> Performing Cluster Check (using node 127.0.0.1:9001)
M: fceba6001b95e2169ddd6622436b213324fe8f77 127.0.0.1:9001
   slots:12288-16383 (4096 slots) master
   1 additional replica(s)
S: 71f5695dc1f4322c8ea9066d1b3cd8bb0ab7a329 192.168.81.100:9001
   slots: (0 slots) slave
   replicates cf74b2d9570665b74525802462c74cf2e072ef99
M: 6f369311b0ca4c503f337c4bb23424eed3eeb188 127.0.0.1:9002
   slots:6827-10922 (4096 slots) master
   1 additional replica(s)
M: 27266dcfd098dfe2a42361d6ab59edf8fb9f5413 192.168.81.100:9003
   slots:0-1364,5461-6826,10923-12287 (4096 slots) master
   1 additional replica(s)
M: cf74b2d9570665b74525802462c74cf2e072ef99 127.0.0.1:9000
   slots:1365-5460 (4096 slots) master
   1 additional replica(s)
S: ac153a3c1fb10d9d502b153c32dec65c85b2f97c 192.168.81.100:9002
   slots: (0 slots) slave
   replicates fceba6001b95e2169ddd6622436b213324fe8f77
S: 2ff22acb1e006b9881abc80238e15b4e3fcefbef 127.0.0.1:9003
   slots: (0 slots) slave
   replicates 27266dcfd098dfe2a42361d6ab59edf8fb9f5413
S: cb8c114d44d289687798508232d31e0a065fdab5 192.168.81.100:9000
   slots: (0 slots) slave
   replicates 6f369311b0ca4c503f337c4bb23424eed3eeb188
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

Ready to move 1366 slots.       # 此次可以迁移1366个槽
  Source nodes:
    M: cf74b2d9570665b74525802462c74cf2e072ef99 127.0.0.1:9000
   slots:1365-5460 (4096 slots) master
   1 additional replica(s)
  Destination node:
    M: 27266dcfd098dfe2a42361d6ab59edf8fb9f5413 192.168.81.100:9003
   slots:0-1364,5461-6826,10923-12287 (4096 slots) master
   1 additional replica(s)
  Resharding plan:
    Moving slot 1365 from cf74b2d9570665b74525802462c74cf2e072ef99
    Moving slot 1366 from cf74b2d9570665b74525802462c74cf2e072ef99
    ...
    Moving slot 2729 from cf74b2d9570665b74525802462c74cf2e072ef99
    Moving slot 2730 from cf74b2d9570665b74525802462c74cf2e072ef99
Do you want to proceed with the proposed reshard plan (yes/no)? yes
Moving slot 1365 from 127.0.0.1:9000 to 192.168.81.100:9003: 
Moving slot 1366 from 127.0.0.1:9000 to 192.168.81.100:9003: 
...
Moving slot 2728 from 127.0.0.1:9000 to 192.168.81.100:9003: 
Moving slot 2729 from 127.0.0.1:9000 to 192.168.81.100:9003: 
Moving slot 2730 from 127.0.0.1:9000 to 192.168.81.100:9003:
[root@mysql ~]# redis-cli -p 9001 cluster nodes             # 查看集群节点信息
71f5695dc1f4322c8ea9066d1b3cd8bb0ab7a329 192.168.81.100:9001 slave cf74b2d9570665b74525802462c74cf2e072ef99 0 1539827870363 5 connected
6f369311b0ca4c503f337c4bb23424eed3eeb188 127.0.0.1:9002 master - 0 1539827868350 9 connected 6827-10922
27266dcfd098dfe2a42361d6ab59edf8fb9f5413 192.168.81.100:9003 master - 0 1539827872375 12 connected 0-2730 5461-6826 10923-12287
fceba6001b95e2169ddd6622436b213324fe8f77 127.0.0.1:9001 myself,master - 0 0 2 connected 12288-16383
cf74b2d9570665b74525802462c74cf2e072ef99 127.0.0.1:9000 master - 0 1539827873385 1 connected 2731-5460         # 还有2731到5460号槽还没有迁移完
ac153a3c1fb10d9d502b153c32dec65c85b2f97c 192.168.81.100:9002 slave fceba6001b95e2169ddd6622436b213324fe8f77 0 1539827867345 6 connected
2ff22acb1e006b9881abc80238e15b4e3fcefbef 127.0.0.1:9003 slave 27266dcfd098dfe2a42361d6ab59edf8fb9f5413 0 1539827869356 12 connected
cb8c114d44d289687798508232d31e0a065fdab5 192.168.81.100:9000 slave 6f369311b0ca4c503f337c4bb23424eed3eeb188 0 1539827871370 9 connected
[root@mysql ~]# redis-trib.rb reshard --from cf74b2d9570665b74525802462c74cf2e072ef99 --to 6f369311b0ca4c503f337c4bb23424eed3eeb188 --slots 2730 127.0.0.1:9002     # 迁移槽,把192.168.81.101:9000剩余的槽迁移到别的节点                     
>>> Performing Cluster Check (using node 127.0.0.1:9002)
M: 6f369311b0ca4c503f337c4bb23424eed3eeb188 127.0.0.1:9002
   slots:6827-10922 (4096 slots) master
   1 additional replica(s)
S: cb8c114d44d289687798508232d31e0a065fdab5 192.168.81.100:9000
   slots: (0 slots) slave
   replicates 6f369311b0ca4c503f337c4bb23424eed3eeb188
S: ac153a3c1fb10d9d502b153c32dec65c85b2f97c 192.168.81.100:9002
   slots: (0 slots) slave
   replicates fceba6001b95e2169ddd6622436b213324fe8f77
S: 2ff22acb1e006b9881abc80238e15b4e3fcefbef 127.0.0.1:9003
   slots: (0 slots) slave
   replicates 27266dcfd098dfe2a42361d6ab59edf8fb9f5413
S: 71f5695dc1f4322c8ea9066d1b3cd8bb0ab7a329 192.168.81.100:9001
   slots: (0 slots) slave
   replicates cf74b2d9570665b74525802462c74cf2e072ef99
M: cf74b2d9570665b74525802462c74cf2e072ef99 127.0.0.1:9000
   slots:2731-5460 (2730 slots) master
   1 additional replica(s)
M: fceba6001b95e2169ddd6622436b213324fe8f77 127.0.0.1:9001
   slots:12288-16383 (4096 slots) master
   1 additional replica(s)
M: 27266dcfd098dfe2a42361d6ab59edf8fb9f5413 192.168.81.100:9003
   slots:0-2730,5461-6826,10923-12287 (5462 slots) master
   1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

Ready to move 2730 slots.
  Source nodes:
    M: cf74b2d9570665b74525802462c74cf2e072ef99 127.0.0.1:9000
   slots:2731-5460 (2730 slots) master
   1 additional replica(s)
  Destination node:
    M: 6f369311b0ca4c503f337c4bb23424eed3eeb188 127.0.0.1:9002
   slots:6827-10922 (4096 slots) master
   1 additional replica(s)
  Resharding plan:
    Moving slot 2731 from cf74b2d9570665b74525802462c74cf2e072ef99
    Moving slot 2732 from cf74b2d9570665b74525802462c74cf2e072ef99
    ...
    Moving slot 5459 from cf74b2d9570665b74525802462c74cf2e072ef99
    Moving slot 5460 from cf74b2d9570665b74525802462c74cf2e072ef99
Do you want to proceed with the proposed reshard plan (yes/no)? yes
Moving slot 2731 from 127.0.0.1:9000 to 127.0.0.1:9002: 
Moving slot 2732 from 127.0.0.1:9000 to 127.0.0.1:9002: 
...
Moving slot 5459 from 127.0.0.1:9000 to 127.0.0.1:9002: 
Moving slot 5460 from 127.0.0.1:9000 to 127.0.0.1:9002: 
[root@mysql ~]# redis-cli -p 9001 cluster nodes     # 查看集群中节点的信息
71f5695dc1f4322c8ea9066d1b3cd8bb0ab7a329 192.168.81.100:9001 slave 6f369311b0ca4c503f337c4bb23424eed3eeb188 0 1539828016103 13 connected
6f369311b0ca4c503f337c4bb23424eed3eeb188 127.0.0.1:9002 master - 0 1539828017610 13 connected 2731-5460 6827-10922
27266dcfd098dfe2a42361d6ab59edf8fb9f5413 192.168.81.100:9003 master - 0 1539828016605 12 connected 0-2730 5461-6826 10923-12287
fceba6001b95e2169ddd6622436b213324fe8f77 127.0.0.1:9001 myself,master - 0 0 2 connected 12288-16383
cf74b2d9570665b74525802462c74cf2e072ef99 127.0.0.1:9000 master - 0 1539828013588 1 connected       # 192.168.81.101:9000上已经没有槽了
ac153a3c1fb10d9d502b153c32dec65c85b2f97c 192.168.81.100:9002 slave fceba6001b95e2169ddd6622436b213324fe8f77 0 1539828015601 6 connected
2ff22acb1e006b9881abc80238e15b4e3fcefbef 127.0.0.1:9003 slave 27266dcfd098dfe2a42361d6ab59edf8fb9f5413 0 1539828014593 12 connected
cb8c114d44d289687798508232d31e0a065fdab5 192.168.81.100:9000 slave 6f369311b0ca4c503f337c4bb23424eed3eeb188 0 1539828012582 13 connected
[root@mysql ~]# redis-cli -p 9001 cluster nodes |grep master
6f369311b0ca4c503f337c4bb23424eed3eeb188 127.0.0.1:9002 master - 0 1539828068951 13 connected 2731-5460 6827-10922
27266dcfd098dfe2a42361d6ab59edf8fb9f5413 192.168.81.100:9003 master - 0 1539828071970 12 connected 0-2730 5461-6826 10923-12287
fceba6001b95e2169ddd6622436b213324fe8f77 127.0.0.1:9001 myself,master - 0 0 2 connected 12288-16383
cf74b2d9570665b74525802462c74cf2e072ef99 127.0.0.1:9000 master - 0 1539828073980 1 connected
[root@mysql ~]# redis-trib.rb del-node 127.0.0.1:9001 cb8c114d44d289687798508232d31e0a065fdab5        # 先移除192.168.81.101:9000的slave节点
>>> Removing node cb8c114d44d289687798508232d31e0a065fdab5 from cluster 127.0.0.1:9001
>>> Sending CLUSTER FORGET messages to the cluster...
>>> SHUTDOWN the node.
[root@mysql ~]# redis-trib.rb del-node 127.0.0.1:9001 cf74b2d9570665b74525802462c74cf2e072ef99        # 移除192.168.81.101:9000节点
>>> Removing node cf74b2d9570665b74525802462c74cf2e072ef99 from cluster 127.0.0.1:9001
>>> Sending CLUSTER FORGET messages to the cluster...
>>> SHUTDOWN the node.
[root@mysql ~]# redis-cli -p 9000       # 连接192.168.81.101:9000客户端,显示连接失败
Could not connect to Redis at 127.0.0.1:9000: Connection refused
Could not connect to Redis at 127.0.0.1:9000: Connection refused
not connected> exit
[root@mysql ~]# redis-trib.rb del-node 127.0.0.1:9001 cf74b2d9570665b74525802462c74cf2e072ef99        # 移除192.168.81.101:9000节点,显示移除失败,因为集群中已经没有这个节点了 
>>> Removing node cf74b2d9570665b74525802462c74cf2e072ef99 from cluster 127.0.0.1:9001
[ERR] No such node ID cf74b2d9570665b74525802462c74cf2e072ef99
[root@mysql ~]# redis-cli -p 9002 cluster nodes     # 查看集群中的节点信息
6f369311b0ca4c503f337c4bb23424eed3eeb188 127.0.0.1:9002 myself,master - 0 0 13 connected 2731-5460 6827-10922
ac153a3c1fb10d9d502b153c32dec65c85b2f97c 192.168.81.100:9002 slave fceba6001b95e2169ddd6622436b213324fe8f77 0 1539828535088 6 connected
2ff22acb1e006b9881abc80238e15b4e3fcefbef 127.0.0.1:9003 slave 27266dcfd098dfe2a42361d6ab59edf8fb9f5413 0 1539828539118 12 connected
71f5695dc1f4322c8ea9066d1b3cd8bb0ab7a329 192.168.81.100:9001 slave 6f369311b0ca4c503f337c4bb23424eed3eeb188 0 1539828536098 13 connected
fceba6001b95e2169ddd6622436b213324fe8f77 127.0.0.1:9001 master - 0 1539828537106 2 connected 12288-16383
27266dcfd098dfe2a42361d6ab59edf8fb9f5413 192.168.81.100:9003 master - 0 1539828538114 12 connected 0-2730 5461-6826 10923-12287

使用官方工具安装集群的优点

  • 简单高效,准确
  • 生产环境可以使用

(三)Redis Cluster特性

在探讨Redis Cluster特性之前,我们要明白为什么要实现Redis Cluster?

  1. 主从复制不能实现高可用
  2. 随着公司发展,用户数量增多,并发越来越多,业务需要更高的QPS,而主从复制中单机的QPS可能无法满足业务需求
  3. 数据量的考虑,现有服务器内存不能满足业务数据的需要时,单纯向服务器添加内存不能达到要求,此时需要考虑分布式需求,把数据分布到不同服务器上
  4. 网络流量需求:业务的流量已经超过服务器的网卡的上限值,可以考虑使用分布式来进行分流
  5. 离线计算,需要中间环节缓冲等别的需求

1.数据分布

做数据分布的意义:
全量数据,单机Redis节点无法满足要求,按照分区规则把数据分到若干个子集当中

在这里插入图片描述

(1)常用数据分布方式之顺序分布
比如:1到100个数字,要保存在3个节点上,按照顺序分区,把数据平均分配三个节点上
1号到33号数据保存到节点1上,34号到66号数据保存到节点2上,67号到100号数据保存到节点3上
在这里插入图片描述
顺序分区常用在关系型数据库的设计

(2)常用数据分布方式之哈希分布
例如1到100个数字,对每个数字进行哈希运算,然后对每个数的哈希结果除以节点数进行取余,余数为1则保存在第1个节点上,余数为2则保存在第2个节点上,余数为0则保存在第3个节点,这样可以保证数据被打散,同时保证数据分布的比较均匀
在这里插入图片描述
哈希分布方式分为三个分区方式:

2 - 1 节点取余分区
比如有100个数据,对每个数据进行hash运算之后,与节点数进行取余运算,根据余数不同保存在不同的节点上

节点取余:hash(key)% nodes
在这里插入图片描述
节点取余方式是非常简单的一种分区方式

节点取余分区方式有一个问题:即当增加或减少节点时,原来节点中的80%的数据会进行迁移操作,对所有数据重新进行分布

节点取余分区方式建议使用多倍扩容的方式,例如以前用3个节点保存数据,扩容为比以前多一倍的节点即6个节点来保存数据,这样只需要适移50%的数据。数据迁移之后,第一次无法从缓存中读取数据,必须先从数据库中读取数据,然后回写到缓存中,然后才能从缓存中读取迁移之后的数据

在这里插入图片描述

节点取余方式优点:

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

节点取余方式缺点:

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

2-2 一致性哈希分区

一致性哈希原理:
将所有的数据当做一个token环,token环中的数据范围是0到2的32次方。然后为每一个数据节点分配一个token范围值,这个节点就负责保存这个范围内的数据。
在这里插入图片描述
对每一个key进行hash运算,被哈希后的结果在哪个token的范围内,则按顺时针去找最近的节点,这个key将会被保存在这个节点上。

在这里插入图片描述
扩容
在这里插入图片描述
在上面的图中,有4个key被hash之后的值在在n1节点和n2节点之间,按照顺时针规则,这4个key都会被保存在n2节点上,
如果在n1节点和n2节点之间添加n5节点,当下次有key被hash之后的值在n1节点和n5节点之间,这些key就会被保存在n5节点上面了。
在上面的例子里,添加n5节点之后,数据迁移会在n1节点和n2节点之间进行,n3节点和n4节点不受影响,数据迁移范围被缩小很多

同理,如果有1000个节点,此时添加一个节点,受影响的节点范围最多只有千分之2

一致性哈希一般用在节点比较多的时候

一致性哈希分区优点:

采用客户端分片方式:哈希 + 顺时针(优化取余)
节点伸缩时,只影响邻近节点,但是还是有数据迁移

一致性哈希分区缺点:

翻倍伸缩,保证最小迁移数据和负载均衡

2-3 虚拟槽分区
虚拟槽分区是Redis Cluster采用的分区方式

预设虚拟槽,每个槽就相当于一个数字,有一定范围。每个槽映射一个数据子集,一般比节点数大

Redis Cluster中预设虚拟槽的范围为0到16383
在这里插入图片描述
步骤:

  1. 把16384槽按照节点数量进行平均分配,由节点进行管理
  2. 对每个key按照CRC16规则进行hash运算
  3. 把hash结果对16383进行取余
  4. 把余数发送给Redis节点
  5. 节点接收到数据,验证是否在自己管理的槽编号的范围
    如果在自己管理的槽编号范围内,则把数据保存到数据槽中,然后返回执行结果
    如果在自己管理的槽编号范围外,则会把数据发送给正确的节点,由正确的节点来把数据保存在对应的槽中

需要注意的是:Redis Cluster的节点之间会共享消息,每个节点都会知道是哪个节点负责哪个范围内的数据槽

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

使用服务端管理节点,槽,数据:例如Redis Cluster
可以对数据打散,又可以保证数据分布均匀

顺序分布与哈希分布的对比

在这里插入图片描述

(四)Redis Cluster基本架构

(1)节点
Redis Cluster是分布式架构:即Redis Cluster中有多个节点,每个节点都负责进行数据读写操作
每个节点之间会进行通信。

(2)meet操作
节点之间会相互通信
meet操作是节点之间完成相互通信的基础,meet操作有一定的频率和规则

在这里插入图片描述
所有的节点共享消息
在这里插入图片描述

(3)指派槽
把16384个槽平均分配给节点进行管理,每个节点只能对自己负责的槽进行读写操作

由于每个节点之间都彼此通信,每个节点都知道另外节点负责管理的槽范围
在这里插入图片描述
客户端访问任意节点时,对数据key按照CRC16规则进行hash运算,然后对运算结果对16383进行取作,如果余数在当前访问的节点管理的槽范围内,则直接返回对应的数据
如果不在当前节点负责管理的槽范围内,则会告诉客户端去哪个节点获取数据,由客户端去正确的节点获取数据

1.keyhash=hash(key)
2. slot=keyhash%16383
在这里插入图片描述
(4)复制
保证高可用,每个主节点都有一个从节点,当主节点故障,Cluster会按照规则实现主备的高可用性

对于节点来说,有一个配置项:cluster-enabled,即是否以集群模式启动

(5) 客户端路由

5-1moved重定向

  1. 每个节点通过通信都会共享Redis Cluster中槽和集群中对应节点的关系
  2. 客户端向Redis Cluster的任意节点发送命令,接收命令的节点会根据CRC16规则进行hash运算与16383取余,计算自己的槽和对应节点
  3. 如果保存数据的槽被分配给当前节点,则去槽中执行命令,并把命令执行结果返回给客户端
  4. 如果保存数据的槽不在当前节点的管理范围内,则向客户端返回moved重定向异常
  5. 客户端接收到节点返回的结果,如果是moved异常,则从moved异常中获取目标节点的信息
  6. 客户端向目标节点发送命令,获取命令执行结果

在这里插入图片描述

需要注意的是:客户端不会自动找到目标节点执行命令

槽命中:直接返回

在这里插入图片描述

[root@mysql ~]# redis-cli -p 9002 cluster keyslot hello
(integer) 866

槽不命中:moved异常

[root@mysql ~]# redis-cli -p 9002 cluster keyslot php
(integer) 9244

在这里插入图片描述

[root@mysql ~]# redis-cli -c -p 9002
127.0.0.1:9002> cluster keyslot hello
(integer) 866
127.0.0.1:9002> set hello world
-> Redirected to slot [866] located at 192.168.81.100:9003
OK
192.168.81.100:9003> cluster keyslot python
(integer) 7252
192.168.81.100:9003> set python best
-> Redirected to slot [7252] located at 192.168.81.101:9002
OK
192.168.81.101:9002> get python
"best"
192.168.81.101:9002> get hello
-> Redirected to slot [866] located at 192.168.81.100:9003
"world"
192.168.81.100:9003> exit
[root@mysql ~]# redis-cli -p 9002
127.0.0.1:9002> cluster keyslot python
(integer) 7252
127.0.0.1:9002> set python best
OK
127.0.0.1:9002> set hello world
(error) MOVED 866 192.168.81.100:9003
127.0.0.1:9002> exit
[root@mysql ~]# 

5-2 ask重定向
在这里插入图片描述
在对集群进行扩容和缩容时,需要对槽及槽中数据进行迁移

当客户端向某个节点发送命令,节点向客户端返回moved异常,告诉客户端数据对应的槽的节点信息

如果此时正在进行集群扩展或者缩空操作,当客户端向正确的节点发送命令时,槽及槽中数据已经被迁移到别的节点了,就会返回ask,这就是ask重定向机制
在这里插入图片描述
步骤:

  1. 客户端向目标节点发送命令,目标节点中的槽已经迁移支别的节点上了,此时目标节点会返回ask转向给客户端
  2. 客户端向新的节点发送Asking命令给新的节点,然后再次向新节点发送命令
  3. 新节点执行命令,把命令执行结果返回给客户端

moved异常与ask异常的相同点和不同点

  • 两者都是客户端重定向
  • moved异常:槽已经确定迁移,即槽已经不在当前节点
  • ask异常:槽还在迁移中

5-3 smart智能客户端
使用智能客户端的首要目标:追求性能

从集群中选一个可运行节点,使用Cluster slots初始化槽和节点映射

将Cluster slots的结果映射在本地,为每个节点创建JedisPool,相当于为每个redis节点都设置一个JedisPool,然后就可以进行数据读写操作

读写数据时的注意事项:

每个JedisPool中缓存了slot和节点node的关系
key和slot的关系:对key进行CRC16规则进行hash后与16383取余得到的结果就是槽
JedisCluster启动时,已经知道key,slot和node之间的关系,可以找到目标节点
JedisCluster对目标节点发送命令,目标节点直接响应给JedisCluster
如果JedisCluster与目标节点连接出错,则JedisCluster会知道连接的节点是一个错误的节点
此时JedisCluster会随机节点发送命令,随机节点返回moved异常给JedisCluster
JedisCluster会重新初始化slot与node节点的缓存关系,然后向新的目标节点发送命令,目标命令执行命令并向JedisCluster响应
如果命令发送次数超过5次,则抛出异常"Too many cluster redirection!"

在这里插入图片描述
6. 多节点命令实现
Redis Cluster不支持使用scan命令扫描所有节点
多节点命令就是在在所有节点上都执行一条命令
批量操作优化

6-1 串行mget
定义for循环,遍历所有的key,分别去所有的Redis节点中获取值并进行汇总,简单,但是效率不高,需要n次网络时间
在这里插入图片描述
6-2串行IO
对串行mget进行优化,在客户端本地做内聚,对每个key进行CRC16hash,然后与16383取余,就可以知道哪个key对应的是哪个槽

本地已经缓存了槽与节点的对应关系,然后对key按节点进行分组,成立子集,然后使用pipeline把命令发送到对应的node,需要nodes次网络时间,大大减少了网络时间开销
在这里插入图片描述
6-3 并行IO
并行IO是对串行IO的一个优化,把key分组之后,根据节点数量启动对应的线程数,根据多线程模式并行向node节点请求数据,只需要1次网络时间
在这里插入图片描述
6-4 hash_tag
将key进行hash_tag的包装,然后把tag用大括号括起来,保证所有的key只向一个node请求数据,这样执行类似mget命令只需要去一个节点获取数据即可,效率更高
在这里插入图片描述
6-5 四种优化方案优缺点分析
在这里插入图片描述
7 故障发现

Redis Cluster通过ping/pong消息实现故障发现:不需要sentinel

ping/pong不仅能传递节点与槽的对应消息,也能传递其他状态,比如:节点主从状态,节点故障等

故障发现就是通过这种模式来实现,分为主观下线和客观下线

7-1 主观下线
某个节点认为另一个节点不可用,‘偏见’,只代表一个节点对另一个节点的判断,不代表所有节点的认知

主观下线流程:

  1. 节点1定期发送ping消息给节点2
  2. 如果发送成功,代表节点2正常运行,节点2会响应PONG消息给节点1,节点1更新与节点2的最后通信时间
  3. 如果发送失败,则节点1与节点2之间的通信异常判断连接,在下一个定时任务周期时,仍然会与节点2发送ping消息
  4. 如果节点1发现与节点2最后通信时间超过node-timeout,则把节点2标识为pfail状态

在这里插入图片描述
7-2 客观下线
在这里插入图片描述
当半数以上持有槽的主节点都标记某节点主观下线时,可以保证判断的公平性

集群模式下,只有主节点(master)才有读写权限和集群槽的维护权限,从节点(slave)只有复制的权限

客观下线流程:

  1. 某个节点接收到其他节点发送的ping消息,如果接收到的ping消息中包含了其他fail节点,这个节点会将主观下线的消息内容添加到自身的故障列表中,故障列表中包含了当前节点接收到的每一个节点对其他节点的状态信息
  2. 当前节点把主观下线的消息内容添加到自身的故障列表之后,会尝试对故障节点进行客观下线操作
    故障列表的周期为:集群的node-timeout * 2,保证以前的故障消息不会对周期内的故障消息造成影响,保证客观下线的公平性和有效性
    在这里插入图片描述

8.故障恢复

8-1 资格检查

对从节点的资格进行检查,只有难过检查的从节点才可以开始进行故障恢复

每个从节点检查与故障主节点的断线时间

超过cluster-node-timeout * cluster-slave-validity-factor数字,则取消资格

cluster-node-timeout默认为15秒,cluster-slave-validity-factor默认值为10
如果这两个参数都使用默认值,则每个节点都检查与故障主节点的断线时间,如果超过150秒,则这个节点就没有成为替换主节点的可能性

8-2准备选举时间

使偏移量最大的从节点具备优先级成为主节点的条件
在这里插入图片描述
8-3 选举投票
对选举出来的多个从节点进行投票,选出新的主节点
在这里插入图片描述

8-4 替换主节点
当前从节点取消复制变成离节点(slaveof no one)
执行cluster del slot撤销故障主节点负责的槽,并执行cluster add slot把这些槽分配给自己
向集群广播自己的pong消息,表明已经替换了故障从节点

8-5 故障转移演练
对某一个主节点执行kill -9 {pid}来模拟宕机的情况

9.Redis Cluster的缺点
当节点数量很多时,性能不会很高

解决方式:使用智能客户端。智能客户端知道由哪个节点负责管理哪个槽,而且当节点与槽的映射关系发生改变时,客户端也会知道这个改变,这是一种非常高效的方式

(五)开发中常见的问题

(1)集群完整性
cluster-require-full-coverage默认为yes,即是否集群中的所有节点都是在线状态且16384个槽都处于服务状态时,集群才会提供服务

集群中16384个槽全部处于服务状态,保证集群完整性

当某个节点故障或者正在故障转移时获取数据会提示:(error)CLUSTERDOWN The cluster is down

建议把cluster-require-full-coverage设置为no

(2)带宽消耗
Redis Cluster节点之间会定期交换Gossip消息,以及做一些心跳检测

官方建议Redis Cluster节点数量不要超过1000个,当集群中节点数量过多时,会产生不容忽视的带宽消耗

消息发送频率:节点发现与其他节点最后通信时间超过cluster-node-timeout /2时,会直接发送PING消息

消息数据量:slots槽数组(2kb空间)和整个集群1/10的状态数据(10个节点状态数据约为1kb)

节点部署的机器规模:集群分布的机器越多且每台机器划分的节点数越均匀,则集群内整体的可用带宽越高

带宽优化:

  • 避免使用’大’集群:避免多业务使用一个集群,大业务可以多集群
  • cluster-node-timeout:带宽和故障转移速度的均衡
  • 尽量均匀分配到多机器上:保证高可用和带宽

(3)Pub/Sub广播
在任意一个cluster节点执行publish,则发布的消息会在集群中传播,集群中的其他节点都会订阅到消息,这样节点的带宽的开销会很大

publish在集群每个节点广播,加重带宽

解决办法:需要使用Pub/Sub时,为了保证高可用,可以单独开启一套Redis Sentinel

(4)集群倾斜

对于分布式数据库来说,存在倾斜问题是比较常见的

集群倾斜也就是各个节点使用的内存不一致

4-1 数据倾斜原因
1.节点和槽分配不均,如果使用redis-trib.rb工具构建集群,则出现这种情况的机会不多

redis-trib.rb info ip:port查看节点,槽,键值分布
redis-trib.rb rebalance ip:port进行均衡(谨慎使用)

2.不同槽对应键值数量差异比较大

CRC16算法正常情况下比较均匀
可能存在hash_tag
cluster countkeysinslot {slot}获取槽对应键值个数

3.包含bigkey:例如大字符串,几百万的元素的hash,set等

在从节点:redis-cli --bigkeys
优化:优化数据结构

4.内存相关配置不一致
hash-max-ziplist-value:满足一定条件情况下,hash可以使用ziplist
set-max-intset-entries:满足一定条件情况下,set可以使用intset
在一个集群内有若干个节点,当其中一些节点配置上面两项优化,另外一部分节点没有配置上面两项优化
当集群中保存hash或者set时,就会造成节点数据不均匀
优化:定期检查配置一致性

5.请求倾斜:热点key
重要的key或者bigkey
Redis Cluster某个节点有一个非常重要的key,就会存在热点问题

4-2 集群倾斜优化:
避免bigkey
热键不要用hash_tag
当一致性不高时,可以用本地缓存+ MQ(消息队列)

(5)集群读写分离

只读连接:集群模式下,从节点不接受任何读写请求

当向从节点执行读请求时,重定向到负责槽的主节点

readonly命令可以读:连接级别命令,当连接断开之后,需要再次执行readonly命令

读写分离:同样的问题:复制延迟,读取过期数据,从节点故障
修改客户端:cluster slaves {nodeId}

(6)数据迁移

官方迁移工具:redis-trib.rb和import

  • 只能从单机迁移到集群
  • 不支持在线迁移:source需要停写
  • 不支持断点续传
  • 单线程迁移:影响深度

在线迁移:

唯品会:redis-migrate-tool
豌豆荚:redis-port

(7) 集群VS单机

集群的限制:

key批量操作支持有限:例如mget,mset必须在一个slot
key事务和Lua支持有限:操作的key必须在一个节点
key是数据分区的最小粒度:不支持bigkey分区
不支持多个数据库:集群模式下只有一个db0
复制只支持一层:不支持树形复制结构
Redis Cluster满足容量和性能的扩展性,很多业务'不需要'
大多数时客户端性能会'降低'
命令无法跨节点使用:mget,keys,scan,flush,sinter等
Lua和事务无法跨节点使用
客户端维护更复杂:SDK和应用本身消耗(例如更多的连接池)

很多场景Redis Sentinel已经够用了


还剩一些拓展的内容,准备放在第四篇。

对了,兄dei,如果你觉得这篇文章可以的话,给俺点个赞再走,管不管?这样可以让更多的人看到这篇文章,对我来说也是一种激励。

如果你有什么问题的话,欢迎留言或者CSDN APP直接与我交流。

发布了189 篇原创文章 · 获赞 3393 · 访问量 51万+

猜你喜欢

转载自blog.csdn.net/qq_42322103/article/details/104172970