【超详细】CentOS-7下部署高可用Redis6(主从复制、哨兵模式、集群)

搭建了一个 Redis 服务器集群,实现主从配置和容灾部署,使得主机出现故障时,可自动进行容灾切换,下面就详细讲解一下如何利用 Redis 来实现。

文章重点

1、Redis 入门简介

2、Redis 安装部署

3、Redis 集群整体架构

4、Redis 主从配置及数据同步

5、Redis 哨兵模式搭建

一、Redis 入门简介

Redis(Remote Dictionary Server 远程字典服务)是一个开源的高性能key-value数据库,它通常被称为数据结构服务器,因为值(value)可以是 字符串(String), 哈希(Hash), 列表(list), 集合(sets) 和 有序集合(sorted sets)等类型,官网:redis.io/

Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。 Redis不仅仅支持简单的key-value类型的数据,同时还提供 list,set,zset,hash等数据结构的存储。 Redis支持数据的备份,即master-slave模式的数据备份。

为什么用Redis?

一个字,快!传统的关系型数据库如 Mysql 等已经不能适用所有的场景了,比如在高并发,访问流量高峰等情况时,数据库很容易崩了。Redis 运行在内存,能起到一个缓冲作用,由于内存的读写速度远快于硬盘,因此 Redis 在性能上比其他基于硬盘存储的数据库有明显的优势。 同时除了快之外,还可应用于集群的自动容灾切换以及数据的读写分离,减轻高并发的压力。

二、Redis主从复制

1、Redis主从复制的概念

主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(Master),后者称为从节点(Slave);数据的复制是单向的,只能由主节点到从节点。

默认情况下,每台Redis服务器都是主节点;且一个主节点可以有零个或多个从节点(0+个从节点),但一个从节点只能有一个主节点。一般主节点负责接收写请求,从节点负责接收读请求,从而实现读写分离。

主从一般部署在不同机器上,复制时存在网络延时问题,使用参数repl-disable-tcp-nodelay选择是否关闭TCP_NODELAY,默认为关闭:

  • 关闭:无论数据大小都会及时同步到从节点,占带宽,适用于主从网络好的场景;
  • 开启:主节点每隔指定时间合并数据为TCP包节省带宽,默认为40毫秒同步一次,适用于网络环境复杂或带宽紧张,如跨机房;


2、Redis主从复制的作用

  • 数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。
  • 故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余。
  • 负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务,分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。
  • 读写分离:主库写、从库读,读写分离不仅可以提高服务器的负载能力,同时可根据需求的变化,改变从库的数量;
  • 高可用基石:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础。

3、Redis主从复制的流程

●【1】若启动一个Slave机器进程,则它会向Master机器发送一个“sync command”命令,请求同步连接。

●【2】无论是第一次连接还是重新连接,Master机器都会启动一个后台进程,将数据快照保存到数据文件中(执行rdb操作),同时Master还会记录修改数据的所有命令并缓存在数据文件中。

●【3】后台进程完成缓存操作之后,Maste机器就会向Slave机器发送数据文件,Slave端机器将数据文件保存到硬盘上,然后将其加载到内存中,接着Master机器就会将修改数据的所有操作一并发送给Slave端机器。若Slave出现故障导致宕机,则恢复正常后会自动重新连接。

●【4】Master机器收到Slave端机器的连接后,将其完整的数据文件发送给Slave端机器,如果Mater同时收到多个Slave发来的同步请求,则Master会在后台启动一个进程以保存数据文件,然后将其发送给所有的Slave端机器,确保所有的Slave端机器都正常。

4、开启主从配置

配置主从可以在命令行或配置文件中配置,上面提到主节点负责写,从节点负责读,因此推荐开启从服务器的只读配置,否则的话在从节点的写操作不会同步到主节点会导致数据不一致:

命令行模式

在从服务器命令行中执行下面的命令即可成为该主服务器的从节点:

#在从服务器执行下面的命令成为或取消成为某节点的从节点
#slaveof  主服务器的IP  端口号
slaveof  host port

#取消成为任何服务器的从服务器
slaveof no one

#从服务器只读(推荐配置)
config set slave-read-only yes

#查看主从信息
info replication

#配置主节点ACL账号密码(Redis6开启ACL的情况)
config set masteruser username
config set masterauth password

slaveof 命令是异步的,不会阻塞。 同时,从服务器现有的数据会先被清空,然后才会同步主服务器的数据。

配置文件

在从服务器配置文件中添加下面的配置然后重启从服务器即可:

#在从节点配置文件中新增下面两个配置即可指定成为某个主节点的从节点
#slaveof 主节点地址 主节点端口
slaveof  host port

#从服务器只读(推荐配置)
slave-read-only yes

三、Redis主从复制的部署

1、环境配置

主机操作系统IP地址软件 / 安装包 / 工具
MasterCentOS710.0.0.11redis-6.2.1.tar.gz
Slave1CentOS710.0.0.12redis-6.2.1.tar.gz
Slave2CentOS710.0.0.13redis-6.2.1.tar.gz

redis下载地址download.redis.io/relea

2、编译安装reids6.2.1-所有主机

1 下载redis源码包

# 安装依赖
yum install -y gcc gcc-c++ make   # 编译所需要的环境和编译器

wget https://download.redis.io/releases/redis-6.2.1.tar.gz
tar xzf redis-6.2.1.tar.gz  -C  /opt
cd /opt/redis-6.2.1

2 创建安装目录

mkdir /usr/local/redis

3 编译并安装到指定目录

make && make PREFIX=/usr/local/redis install

4 执行服务安装脚本

cd /opt/redis-6.2.1/utils
./install_server.sh   # 如果出现报错,按下面注意事项操作

回车四次,下一步需要手动输入
Please select the redis executable path   []   /usr/local/redis/bin/redis-server    

ln -s /usr/local/redis/bin/* /usr/local/bin/

注意事项:安装完成以后redis-server在/usr/local/bin/目录下

输入启动命令./install_server.sh如果报错
解决方案:在该文件目录下  执行  vi/vim  install_server.sh
找到并注释下面的代码即可
#bail if this system is managed by systemd
#_pid_1_exe="$(readlink -f /proc/1/exe)"
#if [ "${_pid_1_exe##*/}" = systemd ]
#then
#       echo "This systems seems to use systemd."
#       echo "Please take a look at the provided example service unit files in this directory, and adapt and install them. Sorry!"
#       exit 1
#fi


3、修改配置文件,搭建redis主从同步

1.配置文件详解

# redis进程是否以守护进程的方式运行,yes为是,no为否(不以守护进程的方式运行会占用一个终端)。
daemonize no
# 指定redis进程的PID文件存放位置
pidfile /var/run/redis.pid
# redis进程的端口号
port 6379
#是否开启保护模式,默认开启。要是配置里没有指定bind和密码。开启该参数后,redis只会本地进行访问,拒绝外部访问。要是开启了密码和bind,可以开启。否则最好关闭设置为no。
protected-mode yes
# 绑定的主机地址
bind 127.0.0.1
# 客户端闲置多长时间后关闭连接,默认此参数为0即关闭此功能
timeout 300
# redis日志级别,可用的级别有debug.verbose.notice.warning
loglevel verbose
# log文件输出位置,如果进程以守护进程的方式运行,此处又将输出文件设置为stdout的话,就会将日志信息输出到/dev/null里面去了
logfile stdout   # 本次实验使用logfile   /var/log/redis_6379.log  
# 设置数据库的数量,默认为0可以使用select <dbid>命令在连接上指定数据库id
databases 16
# 指定在多少时间内刷新次数达到多少的时候会将数据同步到数据文件
save <seconds> <changes>
# 指定存储至本地数据库时是否压缩文件,默认为yes即启用存储
rdbcompression yes
# 指定本地数据库文件名
dbfilename dump.db
# 指定本地数据存放位置
dir ./     # dir /var/lib/redis/6379
# 指定当本机为slave服务时,设置master服务的IP地址及端口,在redis启动的时候他会自动跟master进行数据同步
replicaof <masterip> <masterport>
# 当master设置了密码保护时,slave服务连接master的密码
masterauth <master-password>
# 设置redis连接密码,如果配置了连接密码,客户端在连接redis是需要通过AUTH<password>命令提供密码,默认关闭
requirepass footbared
# 设置同一时间最大客户连接数,默认无限制。redis可以同时连接的客户端数为redis程序可以打开的最大文件描述符,如果设置 maxclients 0,表示不作限制。当客户端连接数到达限制时,Redis会关闭新的连接并向客户端返回 max number of clients reached 错误信息
maxclients 128
# 指定Redis最大内存限制,Redis在启动时会把数据加载到内存中,达到最大内存后,Redis会先尝试清除已到期或即将到期的Key。当此方法处理后,仍然到达最大内存设置,将无法再进行写入操作,但仍然可以进行读取操作。Redis新的vm机制,会把Key存放内存,Value会存放在swap区
maxmemory<bytes>
# 指定是否在每次更新操作后进行日志记录,Redis在默认情况下是异步的把数据写入磁盘,如果不开启,可能会在断电时导致一段时间内的数据丢失。因为redis本身同步数据文件是按上面save条件来同步的,所以有的数据会在一段时间内只存在于内存中。默认为no。
appendonly no  # 开启AOF持久化功能  appendonly yes 
# 指定跟新日志文件名默认为appendonly.aof
appendfilename appendonly.aof
# 指定更新日志的条件,有三个可选参数 - no:表示等操作系统进行数据缓存同步到磁盘(快),always:表示每次更新操作后手动调用fsync()将数据写到磁盘(慢,安全), everysec:表示每秒同步一次(折衷,默认值);
appendfsync everysec

2.修改Master节点Redis配置文件

# vim /etc/redis/6379.conf
bind 0.0.0.0                         # 修改bind 项,0.0.0.0监听所有网段
port:6379                           # 工作端口
protected-mode:no                   # 关闭保护模式
daemonize yes                       # 开启守护进程
logfile /var/log/redis_6379.log     # 指定日志文件目录
dir /var/lib/redis/6379             # 指定本地数据存放位置
appendonly yes                      # 开启AOF持久化功能
requirepass:pwdtest@2021           # 设置 redis 连接密码
masterauth:pwdtest@2021            # 设置slave 服务连接 master 的密码

/etc/init.d/redis_6379 restart  # 重启redis

3.修改Slave节点Redis配置文件

# vim /etc/redis/6379.conf
bind 0.0.0.0                         # 修改bind 项,0.0.0.0监听所有网卡
port:6379                           # 工作端口
protected-mode:no                   # 关闭保护模式
daemonize yes                       # 开启守护进程
logfile /var/log/redis_6379.log     # 指定日志文件目录
dir /var/lib/redis/6379             # 指定本地数据存放位置
appendonly yes                      # 开启AOF持久化功能
requirepass:pwdtest@2021           # 设置 redis 连接密码
masterauth:pwdtest@2021            # 设置slave 服务连接 master 的密码
replicaof 10.0.0.11 6379            # 指定要同步的Master节点IP和端口

# /etc/init.d/redis_6379 restart  # 重启redis

参数:replicaof 10.0.0.11 6379 指定当本机为 slave 服务时,设置 master 服务的IP地址及端口,在 redis 启动的时候会自动跟 master 进行数据同步,所以两台从机都这样配置即可

# 注:由于我们搭建的集群需要自动容灾切换,主数据库可能会变成从数据库,所以三台机器上都需要同时设置 requirepass 和 masterauth 配置项

4、验证主从效果

在Master节点上看日志

tail -f /var/log/redis_6379.log

看到下图结果说明主从配置成功


在Master节点上验证从节点

# 使用命令行,检查从节点
# 命令: redis-cli -a auth_password info replication    

[root@localhost redis-6.2.1]# redis-cli -a pwdtest@2021 info replication
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
# Replication
role:master
connected_slaves:2
slave0:ip=10.0.0.12,port=6379,state=online,offset=812,lag=1
slave1:ip=10.0.0.13,port=6379,state=online,offset=812,lag=1
master_failover_state:no-failover
master_replid:1ace29e6bcfeb13c8ea9af129eb6a736c81b149d
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:812
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:812


5.redis远程连接命令测试服务

# 命令格式:
redis-cli -h yourIp -p yourPort -a youpassword//启动redis客户端,并连接服务器 redis默认端口6379

# 在登录的时候的时候输入密码
redis-cli -h 10.0.0.10 -p 6379 -a foobaa

keys * # 输出服务器中的所有key

# 不重启设置redis密码
redis 127.0.0.1:6379> config set requirepass foo123  # 重启密码失效

# 查询密码:
redis 127.0.0.1:6379> config get requirepass
 (error) ERR operation not permitted

# 验证密码:
redis 127.0.0.1:6379> auth foo123
ok

# 获取密码:
redis 127.0.0.1:6379> config get requirepass
   1) "requirepass"
   2) "foo123"

在主服务器上写入数据

# 主服务器登陆redis
[root@localhost redis-6.2.1]# redis-cli -h 10.0.0.11 -p 6379 -a pwdtest@2021
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
10.0.0.11:6379> keys *
(empty array)
# 此时主服务器数据是空的

# 主服务器写入测试数据

10.0.0.11:6379> set name lisi
OK

#

在从服务器上查询数据

# 从服务器登陆redis
# redis-cli -h 10.0.0.12 -p 6379 -a pwdtest@2021
# redis-cli -h 10.0.0.13 -p 6379 -a pwdtest@2021
[root@haproxy-master redis-6.2.1]# redis-cli -h 10.0.0.12 -p 6379 -a pwdtest@2021
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
10.0.0.12:6379> get name
"lisi"
# 从服务器已经同步

至此,redis主从复制搭建完毕

四、Redis 哨兵模式搭建

哨兵的核心功能:在主从复制的基础上,哨兵引入了主节点的自动故障转移

1、哨兵模式详解

哨兵(sentinel):是一个分布式系统,用于对主从结构中的每台服务器进行监控,当出现故障时通过投票机制选择新的 Master 并将所有 Slave 连接到新的 Master。所以整个运行哨兵的集群的数量不得少于3个节点。

Redis Sentinel是Redis 的高可用性解决方案,由一个或多个Sentinel(哨兵)实例组成。它可以监视任意多个主服务器,以及这些主服务器属下的所有从服务器,并在被监视的主服务器进入下线状态时,自动将下线主服务器属下的某个从服务器升级为新的主服务器,它的主要功能如下:

  • 监控(Monitoring):Sentinel会不断地检查你的主服务器和从服务器是否运作正常。
  • 通知(Notification):当被监控的某个 Redis 服务器出现问题时, Sentinel可以通过API向管理员或者其他应用程序发送通知。
  • 故障迁移:当主服务器不能正常工作时,Sentinel会自动进行故障迁移,也就是主从切换。当主节点不能正常工作时,哨兵会开始自动故障转移操作,它会将失效主节点的其中一个从节点升级为新的主节点,并让其他从节点改为复制新的主节点。
  • 统一的配置管理:连接者询问sentinel取得主从的地址。

2、哨兵原理

Sentinel 使用的算法核心是 Raft 算法,主要用途就是用于分布式系统,系统容错,以及Leader选举,每个Sentinel都需要定期的执行以下任务:

  • 每个 Sentinel 会自动发现其他 Sentinel 和从服务器,它以每秒钟一次的频率向它所知的主服务器、从服务器以及其他 Sentinel 实例发送一个 PING 命令。
  • 如果一个实例(instance)距离最后一次有效回复 PING 命令的时间超过 down-after-milliseconds 选项所指定的值, 那么这个实例会被 Sentinel 标记为主观下线。 有效回复可以是: +PONG 、 -LOADING 或者 -MASTERDOWN 。
  • 如果一个主服务器被标记为主观下线, 那么正在监视这个主服务器的所有Sentinel要以每秒一次的频率确认主服务器的确进入了主观下线状态。
  • 如果一个主服务器被标记为主观下线, 并且有足够数量的Sentinel(至少要达到配置文件指定的数量)在指定的时间范围内同意这一判断, 那么这个主服务器被标记为客观下线。
  • 在一般情况下, 每个Sentinel会以每 10 秒一次的频率向它已知的所有主服务器和从服务器发送 INFO 命令。 当一个主服务器被Sentinel标记为客观下线时,Sentinel向下线主服务器的所有从服务器发送 INFO 命令的频率会从 10 秒一次改为每秒一次。
  • 当没有足够数量的Sentinel同意主服务器已经下线, 主服务器的客观下线状态就会被移除。 当主服务器重新向Sentinel的 PING 命令返回有效回复时, 主服务器的主管下线状态就会被移除。



3、哨兵模式的结构

哨兵结构由两部分组成,哨兵节点和数据节点:

  • 哨兵节点:哨兵系统由一个或多个哨兵节点组成,哨兵节点是特殊的redis节点,不存储数据。
  • 数据节点:主节点和从节点都是数据节点。
  • 哨兵的启动依赖于主从模式,所以须把主从模式安装好的情况下再去做哨兵模式,所有节点上都需要部署哨兵模式,哨兵模式会监控所有的 Redis 工作节点是否正常,当 Master 出现问题的时候,因为其他节点与主节点失去联系,因此会投票,投票过半就认为这个 Master 的确出现问题,然后会通知哨兵间,然后从 Slaves 中选取一个作为新的 Master。

需要特别注意的是,客观下线是主节点才有的概念;如果从节点和哨兵节点发生故障,被哨兵主观下线后,不会再有后续的客观下线和故障转移操作。

4、sentinel.conf配置文件详解

哨兵的配置主要就是修改sentinel.conf配置文件中的参数,在Redis安装目录即可看到此配置文件,各参数详解如下:

# 哨兵sentinel实例运行的端口,默认26379  
port 26379
# 哨兵sentinel的工作目录
dir ./
# 是否开启保护模式,默认开启。
protected-mode:no
# 是否设置为后台启动。
daemonize:yes

# 哨兵sentinel的日志文件
logfile:./sentinel.log

# 哨兵sentinel监控的redis主节点的 
## ip:主机ip地址
## port:哨兵端口号
## master-name:可以自己命名的主节点名字(只能由字母A-z、数字0-9 、这三个字符".-_"组成。)
## quorum:当这些quorum个数sentinel哨兵认为master主节点失联 那么这时 客观上认为主节点失联了  
# sentinel monitor <master-name> <ip> <redis-port> <quorum>  
sentinel monitor mymaster 127.0.0.1 6379 2

# 当在Redis实例中开启了requirepass,所有连接Redis实例的客户端都要提供密码。
# sentinel auth-pass <master-name> <password>  
sentinel auth-pass mymaster 123456  

# 指定主节点应答哨兵sentinel的最大时间间隔,超过这个时间,哨兵主观上认为主节点下线,默认30秒  
# sentinel down-after-milliseconds <master-name> <milliseconds>
sentinel down-after-milliseconds mymaster 30000  

# 指定了在发生failover主备切换时,最多可以有多少个slave同时对新的master进行同步。这个数字越小,完成failover所需的时间就越长;反之,但是如果这个数字越大,就意味着越多的slave因为replication而不可用。可以通过将这个值设为1,来保证每次只有一个slave,处于不能处理命令请求的状态。
# sentinel parallel-syncs <master-name> <numslaves>
sentinel parallel-syncs mymaster 1  

# 故障转移的超时时间failover-timeout,默认三分钟,可以用在以下这些方面:
## 1. 同一个sentinel对同一个master两次failover之间的间隔时间。  
## 2. 当一个slave从一个错误的master那里同步数据时开始,直到slave被纠正为从正确的master那里同步数据时结束。  
## 3. 当想要取消一个正在进行的failover时所需要的时间。
## 4.当进行failover时,配置所有slaves指向新的master所需的最大时间。不过,即使过了这个超时,slaves依然会被正确配置为指向master,但是就不按parallel-syncs所配置的规则来同步数据了
# sentinel failover-timeout <master-name> <milliseconds>  
sentinel failover-timeout mymaster 180000

# 当sentinel有任何警告级别的事件发生时(比如说redis实例的主观失效和客观失效等等),将会去调用这个脚本。一个脚本的最大执行时间为60s,如果超过这个时间,脚本将会被一个SIGKILL信号终止,之后重新执行。
# 对于脚本的运行结果有以下规则:  
## 1. 若脚本执行后返回1,那么该脚本稍后将会被再次执行,重复次数目前默认为10。
## 2. 若脚本执行后返回2,或者比2更高的一个返回值,脚本将不会重复执行。  
## 3. 如果脚本在执行过程中由于收到系统中断信号被终止了,则同返回值为1时的行为相同。
# sentinel notification-script <master-name> <script-path>  
sentinel notification-script mymaster /var/redis/notify.sh

# 这个脚本应该是通用的,能被多次调用,不是针对性的。
# sentinel client-reconfig-script <master-name> <script-path>
sentinel client-reconfig-script mymaster /var/redis/reconfig.sh

5、哨兵搭建

1、环境配置

基于主从复制已搭建完成

主机操作系统IP地址软件 / 安装包 / 工具
MasterCentOS710.0.0.11redis-6.2.1.tar.gz
Slave1CentOS710.0.0.12redis-6.2.1.tar.gz
Slave2CentOS710.0.0.13redis-6.2.1.tar.gz

2、修改 sentinel.conf 配置文件(所有节点操作)

# vim /opt/redis-6.2.1/sentinel.conf           
# 最好复制一份到/etc/redis/sentinel.conf与redis主配置文件一起,方便管理

pidfile /var/run/redis-sentinel.pid             #运行时PID文件
protected-mode no                               # 关闭保护模式
port 26379                                      # Redis哨兵默认的监听端口
daemonize yes                                   # 指定sentinel为后台启动
logfile "/var/log/sentinel.log"                 # 指定日志存放路径
dir "/var/lib/redis/6379"                       # 指定数据库存放路径

#监控的节点名字可以自定义,后边的2代表的:如果有俩个哨兵判断这个主节点挂了那这个主节点就挂了,通常设置为哨兵个数一半加一
# 指定该哨兵节点监控10.0.0.11:6379这个主节点,该主节点的名称是mymaster,最后的2的含义与主节点的故障判定有关:至少需要2个哨兵节点同意,才能判定主节点故障并进行故障转移
sentinel monitor mymaster 10.0.0.11 6379 2      

# 当在Redis实例中开启了requirepass,这里就需要提供密码。
sentinel auth-pass mymaster pwdtest@2021


#哨兵连接主节点多长时间没有响应就代表主节点挂了,单位毫秒。默认30000毫秒,30秒。
sentinel down-after-milliseconds mymaster 30000 

#在故障转移时,最多有多少从节点对新的主节点进行同步。这个值越小完成故障转移的时间就越长,这个值越大就意味着越多的从节点因为同步数据而暂时阻塞不可用
# 主备切换时,最多有多少个slave同时对新的master进行同步,这里设置为默认的1。
sentinel parallel-syncs mymaster 1

# 故障转移的超时时间,这里设置为三分钟180000(180秒)
sentinel failover-timeout mymaster 180000   

#禁止使用SENTINEL SET设置notification-script和client-reconfig-script
sentinel deny-scripts-reconfig yes

3、启动哨兵模式

先启master,再启slave

redis-sentinel /etc/redis/sentinel.conf &

注意!先启动主服务器,再启动从服务器

主服务查看哨兵日志

[root@localhost redis-6.2.1]# tail -f /var/log/sentinel.log
47388:X 06 May 2021 16:19:54.165 # Configuration loaded
47388:X 06 May 2021 16:19:54.166 * Increased maximum number of open files to 10032 (it was originally set to 1024).
47388:X 06 May 2021 16:19:54.166 * monotonic clock: POSIX clock_gettime
47388:X 06 May 2021 16:19:54.166 * Running mode=sentinel, port=26379.
47388:X 06 May 2021 16:19:54.170 # Sentinel ID is 2f8156570dea4ba3b7098c9403271c3821bec416
47388:X 06 May 2021 16:19:54.170 # +monitor master mymaster 10.0.0.11 6379 quorum 2
47388:X 06 May 2021 16:19:54.171 * +slave slave 10.0.0.12:6379 10.0.0.12 6379 @ mymaster 10.0.0.11 6379
47388:X 06 May 2021 16:19:54.190 * +slave slave 10.0.0.13:6379 10.0.0.13 6379 @ mymaster 10.0.0.11 6379
47388:X 06 May 2021 16:20:25.842 * +sentinel sentinel 50359da664bb5f60759ae4883d03ee473a58fcba 10.0.0.12 26379 @ mymaster 10.0.0.11 6379
47388:X 06 May 2021 16:20:28.167 * +sentinel sentinel 2154233aaab70e395abe4eb3178280c66e609e7f 10.0.0.13 26379 @ mymaster 10.0.0.11 6379

看到截图操作结果,哨兵启动成功

主服务查看哨兵信息

[root@localhost redis-6.2.1]# redis-cli -p 26379   info 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=10.0.0.11:6379,slaves=2,sentinels=3
# 最后一行,可以看到,哨兵已经监听到当前的主机IP端口和运行状态,并且有2台从机,3个哨兵 master主机为10.0.0.11

6、故障模拟,容灾切换

此时我们在redis客户端中使用debug命令模拟主节点崩溃的情况,然后看是否会选举10.0.0.12和10.0.0.13提升为主节点,以及10.0.0.11恢复启动后是什么角色:

1 模拟故障

在主节点redis服务器操作

#命令执行一个非法的内存访问从而让 Redis 崩溃,仅在开发时用于 BUG 调试,执行后需要重启服务
# 登陆redis执行  debug segfault  
模拟redis崩溃

[root@localhost redis-6.2.1]# redis-cli -h 10.0.0.11 -p 6379 -a pwdtest@2021 
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
10.0.0.11:6379> debug segfault
Error: Connection reset by peer
(1.11s)
[root@localhost redis-6.2.1]# ps aux | grep redis    # redis崩溃了
root      47388  0.6  0.2 162500 11568 ?        Ssl  16:19   0:05 redis-sentinel *:26379 [sentinel]
root      65131  0.0  0.0 112832  2348 pts/0    S+   16:35   0:00 grep --color=auto redis

2 查看哨兵日志

[root@localhost redis-6.2.1]# tail -f /var/log/sentinel.log
47388:X 06 May 2021 16:35:01.709 # +new-epoch 1
47388:X 06 May 2021 16:35:01.711 # +vote-for-leader 2154233aaab70e395abe4eb3178280c66e609e7f 1
47388:X 06 May 2021 16:35:01.893 # +sdown master mymaster 10.0.0.11 6379
47388:X 06 May 2021 16:35:01.977 # +odown master mymaster 10.0.0.11 6379 #quorum 3/2
47388:X 06 May 2021 16:35:01.977 # Next failover delay: I will not start a failover before Thu May  6 16:41:01 2021
47388:X 06 May 2021 16:35:02.450 # +config-update-from sentinel 2154233aaab70e395abe4eb3178280c66e609e7f 10.0.0.13 26379 @ mymaster 10.0.0.11 6379
47388:X 06 May 2021 16:35:02.450 # +switch-master mymaster 10.0.0.11 6379 10.0.0.13 6379  # 选举10.0.0.13为主节点
47388:X 06 May 2021 16:35:02.450 * +slave slave 10.0.0.12:6379 10.0.0.12 6379 @ mymaster 10.0.0.13 6379
47388:X 06 May 2021 16:35:02.450 * +slave slave 10.0.0.11:6379 10.0.0.11 6379 @ mymaster 10.0.0.13 6379
47388:X 06 May 2021 16:35:32.455 # +sdown slave 10.0.0.11:6379 10.0.0.11 6379 @ mymaster 10.0.0.13 6379
# 也可以使用kill -9 redis进程号  杀掉redis-server进程,只要能停掉redis服务的进程都可以,注意不要把哨兵也一起停了


3 查看哨兵信息

[root@localhost redis-6.2.1]# redis-cli -p 26379 INFO 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=10.0.0.13:6379,slaves=2,sentinels=3
# 最后一行,可以看到,哨兵已经监听到当前的主机IP端口和运行状态,并且有2台从机,3个哨兵  master切换成了10.0.0.13

4 重启故障redis服务后查看

# 启动redis服务
/etc/init.d/redis_6379   start

故障服务器重启后查看redis信息

[root@localhost redis-6.2.1]# redis-cli -p  6379  -a pwdtest@2021 INFO Replication
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
# Replication
role:slave   # 自动变成了从服务
master_host:10.0.0.13  # 主服务
master_port:6379
master_link_status:up
master_last_io_seconds_ago:0
master_sync_in_progress:0
slave_repl_offset:416763
slave_priority:100
slave_read_only:1
connected_slaves:0
master_failover_state:no-failover
master_replid:aeb46aa6fab34107134221acb5d9e1db56f57ede
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:416763
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:300485
repl_backlog_histlen:116279

哨兵已经自动将10.0.0.11节点作为10.0.0.13新主节点的从节点:


到新的master节点查看信息

进入10.0.0.13 6379 可以看到它已经由从机(slave)变为了主机(master),并且成功连接从机

[root@haproxy-backup redis-6.2.1]# redis-cli -p  6379  -a pwdtest@2021 INFO Replication
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
# Replication
role:master
connected_slaves:2
slave0:ip=10.0.0.12,port=6379,state=online,offset=611769,lag=0
slave1:ip=10.0.0.11,port=6379,state=online,offset=611769,lag=0
master_failover_state:no-failover
master_replid:aeb46aa6fab34107134221acb5d9e1db56f57ede
master_replid2:1ace29e6bcfeb13c8ea9af129eb6a736c81b149d
master_repl_offset:611902
second_repl_offset:175505
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:351
repl_backlog_histlen:611552
[1]+  完成                  redis-sentinel /etc/redis/sentinel.conf

需要注意的是,主从切换后配置文件已经被自动进行了更改,我们现在看一下新上位的主机 redis 日志,如下

[root@haproxy-backup redis-6.2.1]# tail -20 /var/log/redis_6379.log 
3052:S 06 May 2021 16:35:00.847 * Connecting to MASTER 10.0.0.11:6379
3052:S 06 May 2021 16:35:00.848 * MASTER <-> REPLICA sync started
3052:S 06 May 2021 16:35:00.848 # Error condition on socket for SYNC: Connection refused
3052:S 06 May 2021 16:35:01.863 * Connecting to MASTER 10.0.0.11:6379
3052:S 06 May 2021 16:35:01.863 * MASTER <-> REPLICA sync started
3052:S 06 May 2021 16:35:01.865 # Error condition on socket for SYNC: Connection refused
3052:M 06 May 2021 16:35:02.003 * Discarding previously cached master state.
3052:M 06 May 2021 16:35:02.004 # Setting secondary replication ID to 1ace29e6bcfeb13c8ea9af129eb6a736c81b149d, valid up to offset: 175505. New replication ID is aeb46aa6fab34107134221acb5d9e1db56f57ede
3052:M 06 May 2021 16:35:02.004 * MASTER MODE enabled (user request from 'id=9 addr=10.0.0.13:34850 laddr=10.0.0.13:6379 fd=12 name=sentinel-2154233a-cmd age=876 idle=1 flags=x db=0 sub=0 psub=0 multi=4 qbuf=188 qbuf-free=40766 argv-mem=4 obl=45 oll=0 omem=0 tot-mem=61468 events=r cmd=exec user=default redir=-1')
3052:M 06 May 2021 16:35:02.007 # CONFIG REWRITE executed with success.    # 重写了配置文件
3052:M 06 May 2021 16:35:02.506 * Replica 10.0.0.12:6379 asks for synchronization
3052:M 06 May 2021 16:35:02.506 * Partial resynchronization request from 10.0.0.12:6379 accepted. Sending 156 bytes of backlog starting from offset 175505.
3052:M 06 May 2021 16:45:35.384 * Replica 10.0.0.11:6379 asks for synchronization
3052:M 06 May 2021 16:45:35.384 * Partial resynchronization not accepted: Replication ID mismatch (Replica asked for '842427e4569ec111c3723ee6a20c6762139f0cc7', my replication IDs are 'aeb46aa6fab34107134221acb5d9e1db56f57ede' and '1ace29e6bcfeb13c8ea9af129eb6a736c81b149d')
3052:M 06 May 2021 16:45:35.384 * Starting BGSAVE for SYNC with target: disk
3052:M 06 May 2021 16:45:35.385 * Background saving started by pid 123436
123436:C 06 May 2021 16:45:35.387 * DB saved on disk
123436:C 06 May 2021 16:45:35.388 * RDB: 0 MB of memory used by copy-on-write
3052:M 06 May 2021 16:45:35.458 * Background saving terminated with success
3052:M 06 May 2021 16:45:35.459 * Synchronization with replica 10.0.0.11:6379 succeeded

可以看到,当主机挂了的时候,一直连接主机被拒绝,当哨兵选举它为主机后,它成功执行重写的配置文件,并且连接了其他从机。

至此,主从+哨兵的集群架构搭建完毕。

7、原理及说明

哨兵之间会有通讯,哨兵和主从节点之间也有监控,基于这些信息同步和状态监控实现Redis的故障转移:

  • 哨兵和哨兵之间以及哨兵和Redis主从节点之间每隔一秒发送ping监控它们的健康状态;
  • 哨兵向Redis主从节点每隔10秒发送一次info保存节点信息;
  • 哨兵向Redis主节点每隔2秒发送一次hello,直到哨兵报出sdown,代表主节点失联,然后通知其余哨兵尝试连接该主节点

Redis主节点下线的情况分为主观下线和客观下线:

主观下线(sdown):单独一个哨兵发现master故障了。

客观下线(odown):半数哨兵都认为master节点故障就会触发故障转移。

哨兵Leader选举:

一般情况下当哨兵发现主节点sdown之后 该哨兵节点会成为领导者负责处理主从节点的切换工作:

1 哨兵A发现Redis主节点失联;

2 哨兵A报出sdown,并通知其他哨兵,发送指令sentinel is-master-down-by-address-port给其余哨兵节点;

3 其余哨兵接收到哨兵A的指令后尝试连接Redis主节点,发现主节点确实失联;

4 哨兵返回信息给哨兵A,当超过半数的哨兵认为主节点下线后,状态会变成odown;

5 最先发现主节点下线的哨兵A会成为哨兵领导者负责这次的主从节点的切换工作;

哨兵的选举机制是以各哨兵节点接收到发送*sentinel is-master-down-by-address-port*指令的哨兵id 投票,票数最高的哨兵id会成为本次故障转移工作的哨兵Leader;

故障转移:

当哨兵发现主节点下线之后经过上面的哨兵选举机制,选举出本次故障转移工作的哨兵节点完成本次主从节点切换的工作:

  1. 哨兵Leader 根据一定规则从各个从节点中选择出一个节点升级为主节点;
  2. 其余从节点修改对应的主节点为新的主节点;
  3. 当原主节点恢复启动的时候,变为新的主节点的从节点


哨兵Leader选择新的主节点遵循下面几个规则:

健康度:从节点响应时间快;

完整性:从节点消费主节点的offset偏移量尽可能的高 ();

稳定性:若仍有多个从节点,则根据从节点的创建时间选择最有资历的节点升级为主节点;

在哨兵模式下主从节点总是会变更,因此在Java或Python中访问哨兵模式下的Redis时可以使用对应的哨兵接口连接:

#Java
JedisSentinelPool

#Python
from redis.sentinel import SentinelConnectionPool

五、搭建Redis 群集模式

集群,即Redis Cluster,是Redis 3.0开始引入的分布式存储方案

集群由多个节点(Node)组成,Redis的数据分布在这些节点中。集群中的节点分为主节点和从节点:只有主节点负责读写请求和集群信息的维护;从节点只进行主节点数据和状态信息的复制。

1、集群的作用

(1)数据分区:数据分区(或称数据分片)是集群最核心的功能。 集群将数据分散到多个节点,一方面突破了Redis单机内存大小的限制,存储容量大大增加;另一方面每个主节点都可以对外提供读服务和写服务,大大提高了集群的响应能力。 Redis单机内存大小受限问题,在介绍持久化和主从复制时都有提及;例如,如果单机内存太大,bgsave和bgrewriteaof的fork操作可能导致主进程阻塞,主从环境下主机切换时可能导致从节点长时间无法提供服务,全量复制阶段主节点的复制缓冲区可能溢出。

(2)高可用:集群支持主从复制和主节点的自动故障转移(与哨兵类似);当任一节点发生故障时,集群仍然可以对外提供服务。

2、Redis集群的数据分片

Redis集群引入了哈希槽的概念 Redis集群有16384个哈希槽(编号0-16383) 集群的每个节点负责一部分哈希槽 每个Key通过CRC16校验后对16384取余来决定放置哪个哈希槽,通过这个值,去找到对应的插槽所对应的节点,然后直接自动跳转到这个对应的节点上进行存取操作

以3个节点组成的集群为例:

节点A包含0到5460号哈希槽 节点B包含5461到10922号哈希槽 节点C包含10923到16383号哈希槽

Redis集群的主从复制模型

集群中具有A、B、C三个节点,如果节点B失败了,整个集群就会因缺少5461-10922这个范围的槽而不可以用。 为每个节点添加一个从节点A1、B1、C1整个集群便有三个Master节点和三个slave节点组成,在节点B失败后,集群选举B1位为的主节点继续服务。当B和B1都失败后,集群将不可用

3、搭建Redis 群集

redis的集群一般需要6个节点,3主3从。方便起见,这里所有节点在6台服务器上模拟: 以IP及端口号进行区分:3个主节点端口号:7001、7003、7005,对应的从节点端口号:7002、7004、7006。

六台服务器都需要安装redis数据库

主机操作系统IIP:端口软件 / 安装包 / 工具
Master1CentOS710.0.0.11:7001redis-6.2.1.tar.gz
Slave1CentOS710.0.0.12:7002redis-6.2.1.tar.gz
Master2CentOS710.0.0.13:7003redis-6.2.1.tar.gz
Slave2CentOS710.0.0.14:7004redis-6.2.1.tar.gz
Master3CentOS710.0.0.15:7005redis-6.2.1.tar.gz
Slave3CentOS710.0.0.16:7006redis-6.2.1.tar.gz

1、所有节点

cd /etc/redis/

mkdir -p  redis-cluster/redis6379

cp  /opt/redis-6.2.1/redis.conf       /etc/redis/redis-cluster/redis6379/

cp  /opt/redis-6.2.1/src/redis-cli    /opt/redis-redis-6.2.1/src/redis-server    /etc/redis/redis-cluster/redis6379/

2、Master1节点

#其他5个文件夹的配置文件以此类推修改,注意6个端口都要不一样。
cd /etc/redis/redis-cluster/redis6379
vim redis.conf

bind 10.0.0.11                          # 修改bind项,监听自己的IP
protected-mode no                       # 修改,关闭保护模式
port 7001                               # 修改,redis监听端口,
daemonize yes                           # 以独立进程启动
cluster-enabled yes                     # 取消注释,开启群集功能
cluster-config-file nodes-6379.conf     # 取消注释,群集名称文件设置,无需修改
cluster-node-timeout 15000              # 取消注释群集超时时间设置
appendonly yes                          # 修改,开启AOF持久化

3、其余节点

vim /etc/redis/redis-cluster/redis6379/redis.conf

# 修改bind绑定ip为自己的ip,端口port为自定义端口,保证唯一即可

4、所有节点

启动redis节点

cd /etc/redis/redis-cluster/redis6379/

redis-server    redis.conf

启动redis集群

redis-cli --cluster create 10.0.0.11:7001 10.0.0.12:7002 10.0.0.13:7003 10.0.0.14:7004 10.0.0.15:7005  10.0.0.16:7006  --cluster-replicas 1   # 只有一个副本,如需要多个副本,改变1即可
redis-cli --cluster create 10.0.0.11:7001 10.0.0.12:7002 10.0.0.13:7003 10.0.0.14:7004 10.0.0.15:7005  10.0.0.16:7006  --cluster-replicas 1 -a your_redis_passwrod
# 如果对redis访问设置了密码,则需要加入-a password才能执行
[root@localhost redis6379]# redis-cli  --cluster create 10.0.0.11:7001 10.0.0.11:7002 10.0.0.12:7003 10.0.0.12:7004 10.0.0.13:7005  10.0.0.13:7006  --cluster-replicas 1 
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
# 操作成功如下
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 10.0.0.14:7004 to 10.0.0.11:7001
Adding replica 10.0.0.16:7006 to 10.0.0.12:7002
Adding replica 10.0.0.12:7002 to 10.0.0.13:7003
M: ee2a89ab243c5a155ef52a1f6f5850b2bf029e0e 10.0.0.11:7001
   slots:[0-5460] (5461 slots) master
S: c3175548c975c9087832ec00cfba77fb209b4105 10.0.0.12:7002
   replicates 8f687aaaad3efdf702d1275ab741d3acf74d4751
M: a7bbadb70010d06bd40c9ef920cffa6bf02aeb6d 10.0.0.13:7003
   slots:[5461-10922] (5462 slots) master
S: 9a78a5334acfaf790577d52ebcf7bddeec86fcf4 10.0.0.14:7004
   replicates ee2a89ab243c5a155ef52a1f6f5850b2bf029e0e
M: 8f687aaaad3efdf702d1275ab741d3acf74d4751 10.0.0.15:7005
   slots:[10923-16383] (5461 slots) master
S: c71b804f9b5a2e8d12266ac2697e66faef9fb9b9 10.0.0.16:7006
   replicates a7bbadb70010d06bd40c9ef920cffa6bf02aeb6d

   Can I set the above configuration? (type 'yes' to accept): yes  # 此处输入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 10.0.0.11:7001)
M: ee2a89ab243c5a155ef52a1f6f5850b2bf029e0e 10.0.0.11:7001
   slots:[0-5460] (5461 slots) master
   1 additional replica(s)
M: 8f687aaaad3efdf702d1275ab741d3acf74d4751 10.0.0.15:7005
   slots:[10923-16383] (5461 slots) master
   1 additional replica(s)
S: c3175548c975c9087832ec00cfba77fb209b4105 10.0.0.12:7002
   slots: (0 slots) slave
   replicates 8f687aaaad3efdf702d1275ab741d3acf74d4751
S: 9a78a5334acfaf790577d52ebcf7bddeec86fcf4 10.0.0.14:7004
   slots: (0 slots) slave
   replicates ee2a89ab243c5a155ef52a1f6f5850b2bf029e0e
S: c71b804f9b5a2e8d12266ac2697e66faef9fb9b9 10.0.0.16:7006
   slots: (0 slots) slave
   replicates a7bbadb70010d06bd40c9ef920cffa6bf02aeb6d
M: a7bbadb70010d06bd40c9ef920cffa6bf02aeb6d 10.0.0.13:7003
   slots:[5461-10922] (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.

注意,如果出现如下报错

[ERR] Node 10.0.0.11:7001 is not empty. Either the nodealready knows other nodes (check with CLUSTER NODES) or contains some key in database 0.

解决方法:

1)、将需要新增的节点下aof、rdb等本地备份文件删除; # 该文件在redis数据保存到本地的目录 如/var/lib/redis/等

2)、同时将新Node的集群配置文件删除,即:删除你redis.conf里面cluster-config-file所在目录的文件;

3)、再次添加新节点如果还是报错,则登录新Node,./redis-cli–h x –p对数据库进行清除:

10.0.0.11:7001>  flushdb      #清空当前数据库

测试查看集群服务

redis-cli -h 10.0.0.11 -p 7001 -c   # 加-c参数,节点之间就可以互相跳转 ,不加-c则无法跳转
cluster slots             # 查看节点的哈希槽编号范围
set sky blue              # 设置一个键值
cluster keyslot  sky      # 查看name键的槽编号

查看节点哈希槽编号范围

[root@localhost redis6379]# redis-cli -h 10.0.0.11 -p 7001 -c -a foobaa  # 设置了redis访密码,所访问必须加密码
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
10.0.0.11:7001> cluster slots
1) 1) (integer) 10923
   2) (integer) 16383
   3) 1) "10.0.0.15"
      2) (integer) 7005
      3) "8f687aaaad3efdf702d1275ab741d3acf74d4751"
   4) 1) "10.0.0.12"
      2) (integer) 7002
      3) "c3175548c975c9087832ec00cfba77fb209b4105"
2) 1) (integer) 0
   2) (integer) 5460
   3) 1) "10.0.0.11"
      2) (integer) 7001
      3) "ee2a89ab243c5a155ef52a1f6f5850b2bf029e0e"
   4) 1) "10.0.0.14"
      2) (integer) 7004
      3) "9a78a5334acfaf790577d52ebcf7bddeec86fcf4"
3) 1) (integer) 5461
   2) (integer) 10922
   3) 1) "10.0.0.13"
      2) (integer) 7003
      3) "a7bbadb70010d06bd40c9ef920cffa6bf02aeb6d"
   4) 1) "10.0.0.16"
      2) (integer) 7006
      3) "c71b804f9b5a2e8d12266ac2697e66faef9fb9b9"

设置测试键值并查看

10.0.0.11:7001> set sky bluelight  # 设置一个键值
-> Redirected to slot [14646] located at 10.0.0.15:7005
OK   # 键值对落在了14646,在第一组范围内[10923-16383],第一组M 10.0.0.15 从10.0.0.12

# 此处的主机和端口已自动切换为10.0.0.15:7005
10.0.0.15:7005> cluster keyslot  sky   # 查看name键为sky的槽编号
(integer) 14646



# 重新登录10.0.0.11:7001
[root@localhost redis6379]# redis-cli -h 10.0.0.11 -p 7001 -c -a foobaa
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
10.0.0.11:7001> keys *
(empty array)
10.0.0.11:7001> get sky
-> Redirected to slot [14646] located at 10.0.0.15:7005
"bluelight"    
10.0.0.15:7005>    # 执行完查询自动跳转到10.0.0.15:7005

至此,redis集群搭建完毕

编辑于 2021-06-10 10:38