社区微信群开通啦,扫一扫抢先加入社区官方微信群
社区微信群
因为内容太多了,CSDN编辑器不允许我在一篇文章上发挥。这是头两篇。
介绍了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)两种主从配置方式比较
(一)两个概念
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命令的步骤:
psync ? -1
命令向master发起同步请求可以用下图表示:
全量复制的开销非常大
除了上面提到的开销,如果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分配到不同的服务器上
主从复制高可用的作用:
但是主从架构有一个问题:
如果master宕机,故障转移需要手动完成或者由别的工具来完成,从slave中选择一个slave做为新的master
举个例子:在一主两从情况下,master宕机,则slave从master同步数据也断开,此时client向master写入数据会失败,读写分离时读取数据正常,但不能更新数据。
master出现故障之后,采用手动进行故障转移步骤
这里就引出我们的主角-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故障转移步骤:
在上面的步骤里,sentinel实现了Redis的故障自动发现,自动转移和自动通知
说明:一套Redis sentinel集合可以通知master-name做为标识同时监控多套主从架构
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的请求响应流程,可以知道客户端实现高可用步骤:
python客户端接入Redis Sentinel需要两个参数:sentinel地址集合,masterName
需要注意的是Redis节点的配置文件中的protected-mode必须设置为yes,否则连接会失败
1.Redis Sentinel内部的三个定时任务
Redis Sentinel内部有三个定时任务来对redid节点进行故障判断和转移
sentinel在master节点执行info replication命令,从命令执行结果中解析出slave节点
图为 第一个定时:每10秒info
master节点上有一个发布订阅的channel频道:__sentinel__:hello
,用于所有sentinel之间进行信息交换
一个sentinel发布消息,消息包含当前sentinel节点的信息,对其他sentinel节点的判断以及当前sentinel对master节点和slave节点的一些判断,其他sentinel都可以接收到这条消息。新加入sentinel节点时,sentinel节点之间可以相互感知,以达到信息交互的功能
图为: 第二个定时:每2秒发布订阅
每个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__:hello
频道中交流对master节点的看法,如果sentinel节点都对master节点ping失败’达成共识’,sentinel个数超过quorum的个数,sentinel集合则会认为master节点客观下线3.sentinel领导者选举
只需要一个sentinel节点就可以完成故障转移,通过sentinel is-master-down-by-addr
命令来完成sentinel交换对master节点的失败判定和新master的选举
完成sentinel领导者选举步骤:
4.故障转移(由sentinel领导者节点完成)
故障转移步骤:
slaveof no one
命令,使成为新的master节点5 slave节点的选择
slave节点选择规则
6.Redis Sentine小总结:
RedisCluster 是 Redis 的亲⼉⼦,它是 Redis 作者⾃⼰提供的Redis 集群化⽅案。
搭建Redis Cluster主要步骤
环境说明
两台虚拟机,IP地址分别为:192.168.81.100和192.168.81.101
两台虚拟机操作系统均为:CentOS 7.5 64位
两台虚拟机系统说明:
使用两台虚拟机的7000,7001,7002端口搭建三主三从的Redis Cluster
搭建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
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!