模式一:主从复制,是redis高可用的基础,实现了数据的多机备份,以及对于读操作的负载均衡和简单的故障恢复。
缺陷:故障恢复无法自动化、写操作无法负载均衡、存储能力受到单机的限制。
模式二:哨兵模式,基于主从复制模式,实现了自动化的故障恢复
缺陷:写操作无法负载均衡、存储能力受到单机的限制。
模式三:cluster集群模式,通过Redis集群解决了写操作无法负载均衡,以及存储能力受到单机限制的问题,实现了较为完善的高可用方案。
第一步:slave服务器向master服务器发送sync_command命令,请求同步数据
第二步:master接收到sync_command后使用fork函数派生一个子进程(此过程会阻塞子进程),用于生成RDB文件,并且把客户端执行的写命令保存在缓冲区中,再保存到aof文件中
第三步:master RDB持久化完成之后,将生成的RDB和AOF文件发送给slave
第四步:slave恢复RDB和AOF文件中的数据
第五步:master持续性的将客户端写入的命令以一定的规则同步给slave
哨兵模式主要功能:
① 集群监控:负责监控Redis 主 和 备 进程是否正常工作
② 消息通知:如果某个Redis实例有故障,那么哨兵负责发送消息作为告警通知给管理员
③ 故障转移:如果 主节点 挂掉了,会自动转移到 备节点 上
④ 配置中心:如果故障转移发生了,通知客户端新的master地址
实现步骤:
步骤一:在所有哨兵节点上配置master节点
步骤二:哨兵节点会和配置的主节点建立起两条连接①命令连接、②订阅连接
步骤三:哨兵会通过命令连接周期性(间隔10秒)发送一次info命令,通过info命令,主节点会返回自己的run_id和自己的从节点信息;哨兵通过获取的从节点信息,也会对从节点建立起两条连接:命令连接 和 订阅连接,执行相同的操作。
步骤四:主从节点通过命令连接向哨兵服务器的hello频道发送一条消息,内容包括自己的ip端口、run_id、配置等,用于后续投票使用
步骤五:哨兵集群通过订阅连接对哨兵服务器的hello频道进行监听,所有向该频道发送的消息都能被所有哨兵服务器接受到
步骤六:哨兵集群中每一台哨兵都解析监听到的消息,进行分析提取,就可以知道还有那些别的哨兵服务节点也在监听这些主从节点了,更新结构体将这些哨兵节点记录下来
步骤七:哨兵集群中每一台哨兵都向观察到的其他的哨兵节点建立命令连接(用于投票),没有订阅连接
哨兵模式下的故障迁移
核心:数据分区,也就是分布式方式存储数据,一方面突破了 Redis 单机内存大小的限制,存储容量大大增加,另一方面每个主节点都可以对外提供读服务和写服务,大大提高了集群的响应能力
高可用:集群支持主从复制和主节点的自动故障转移(与哨兵类似),当任意节点发送故障时,集群仍然可以对外提供服务
数据分片:Redis 集群引入了哈希槽的概念,有 16384 个哈希槽(编号 0~16383)集群的每个节点负责一部分哈希槽(平均分配到每个节点),每个 Key 通过 CRC16 校验后对 16384 取余来决定放置哪个哈希槽,通过这个值,去找到对应的插槽所对应的节点,然后直接自动跳转到这个对应的节点上进行存取操作
安装依赖包,下载redis源码包,并解压安装
yum -y install gcc gcc-c++ make
wget -P /opt http://download.redis.io/releases/redis-5.0.9.tar.gz //下载
cd /opt && tar -xzvf redis-5.0.9.tar.gz //解压
cd redis-5.0.9
make && make PREFIX=/usr/local/redis install //安装
cd 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/ //执行的命令优化
netstat -napt | grep 6379 //检查redis是否启动
实验环境:
master服务器:192.168.177.130
slave1服务器:192.168.177.120
slave2服务器:192.168.177.113
步骤一:三台redis服务器关闭防火墙、selinux、安装ntp并且同步时钟
systemctl stop firewalld
systemctl disable firewalld
setenforce 0
yun -y install ntp
ntpdate ntp.aliyun.com
步骤二:修改master配置文件
vim /etc/redis/6379.conf
//定位70行,修改监听地址为:0.0.0.0
70 bind 0.0.0.0
//定位137行,开启守护进程
137 daemonize yes
//定位172行,指定日志文件目录
172 logfile "/var/log/redis_6379.log"
//定位264行,指定工作目录
264 dir "/var/lib/redis/6379"
//定位700行,开启AOF持久化
700 appendonly yes
:wq //保存退出之后重启redis服务器
service redis_6379 restart
步骤三:修改slave1、slave2配置文件(配置相同)
配置与master基本相同,唯一不同的是slave上需要指定同步的master的IP+端口
vim /etc/redis/6379.conf
//定位70行,修改监听地址为:0.0.0.0
70 bind 0.0.0.0
//定位137行,开启守护进程
137 daemonize yes
//定位172行,指定日志文件目录
172 logfile "/var/log/redis_6379.log"
//定位264行,指定工作目录
264 dir "/var/lib/redis/6379"
//定位288行,指定需要同步的master服务器的IP+端口
288 replicaof 192.168.177.130 6379
//定位700行,开启AOF持久化
700 appendonly yes
:wq //保存退出之后重启redis服务器
service redis_6379 restart
步骤四:查看主从同步是否成功
在master节点上使用 redis-cli info replication 命令
[root@master ~]# redis-cli info replication
# Replication
role:master //角色是master
connected_slaves:2 //从服务器数量2
slave0:ip=192.168.177.120,port=6379,state=online,offset=278175,lag=0 //从服务的IP+端口及偏移量
slave1:ip=192.168.177.113,port=6379,state=online,offset=278030,lag=0
master_replid:6bdc666ee61af873e3596ec4f391dc5ff0f96c03 //master启动时生成的40位16进制的随机字符串,用来标识master节点
master_replid2:0000000000000000000000000000000000000000 //切换主从的时候master节点标识会有更改
master_repl_offset:278175 //偏移量,相当于MySQL主从同步中的position
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:278175
实验环境:(基于以上的主从同步增加三台哨兵服务器)
sentinel-01:192.168.177.100
sentinel-02:192.168.177.110
sentinel-03:192.168.177.111
步骤一:所有哨兵节点关闭防火墙、selinux、安装ntp并且同步时钟(同上,代码就不重复贴了)
步骤二:所有哨兵节点修改配置文件
vim /opt/redis-5.0.9/sentinel.conf
//定位17行,关闭保护
17 protected-mode no
//定位21行,是哨兵默认的监听端口不用修改
21 port 26379
//定位26行,开启守护进程
26 daemonize yes
//定位36行,指定日志存放路径
36 logfile "/var/log/sentinel.log"
//定位65行,指定数据库存放路径
65 dir "/var/lib/redis/6379"
//定位84行,指定哨兵节点监控的master节点的IP+端口 2表示至少需要 2 个哨兵节点同意,才能判定主节点故障并进行
84 sentinel monitor mymaster 192.168.226.128 6379 2
//定位113行,判定服务器down掉的时间周期,默认30000毫秒 (30秒 )
113 sentinel down-after-milliseconds mymaster 3000
//定位146行,故障节点的最大超时时间为180000 (180秒)
146 sentinel failover-timeout mymaster 180000
步骤三:启动哨兵并查看监控信息
cd /opt/redis-5.0.9/ && redis-sentinel sentinel.conf //启动哨兵
[root@sentinel-01 ~]# netstat -napt | grep 26379 //查看哨兵是否启动
tcp 0 0 0.0.0.0:26379 0.0.0.0:* LISTEN 81833/redis-sentine
tcp 0 0 192.168.177.100:26379 192.168.177.110:34750 ESTABLISHED 81833/redis-sentine
tcp 0 0 192.168.177.100:51438 192.168.177.111:26379 ESTABLISHED 81833/redis-sentine
tcp 0 0 192.168.177.100:26379 192.168.177.111:58378 ESTABLISHED 81833/redis-sentine
tcp 0 0 192.168.177.100:57282 192.168.177.110:26379 ESTABLISHED 81833/redis-sentine
tcp6 0 0 :::26379 :::* LISTEN 81833/redis-sentine
[root@sentinel-01 ~]# 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=192.168.177.130:6379,slaves=2,sentinels=3
步骤四:在主从复制的master节点上模拟故障
[root@master ~]# netstat -napt | grep 6379
tcp 0 0 0.0.0.0:6379 0.0.0.0:* LISTEN 21865/redis-server
tcp 0 0 192.168.177.130:6379 192.168.177.100:46564 ESTABLISHED 21865/redis-server
tcp 0 0 192.168.177.130:6379 192.168.177.120:41592 ESTABLISHED 21865/redis-server
tcp 0 0 192.168.177.130:6379 192.168.177.110:59928 ESTABLISHED 21865/redis-server
tcp 0 0 192.168.177.130:6379 192.168.177.110:59930 ESTABLISHED 21865/redis-server
tcp 0 0 192.168.177.130:6379 192.168.177.111:48266 ESTABLISHED 21865/redis-server
tcp 0 0 192.168.177.130:6379 192.168.177.111:48264 ESTABLISHED 21865/redis-server
tcp 0 0 192.168.177.130:6379 192.168.177.100:46562 ESTABLISHED 21865/redis-server
tcp 0 0 192.168.177.130:6379 192.168.177.113:44969 ESTABLISHED 21865/redis-server
[root@master ~]# kill -9 21865
步骤五:在哨兵节点上(随便哪一个)监控sentinel.log日志文件查看master切换变化,如下图:
在故障切换过程中,status的状态会经过 sdown——》odown——》ok,可以使用 watch -n 1 redis-cli -p 26379 info sentinel 命令间隔一秒查看
[root@sentinel-03 ~]# redis-cli -p 26379 info sentinel //再次查看master节点信息
# 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.177.113:6379,slaves=2,sentinels=3 //已经切换到113节点了,状态ok
[root@sentinel-03 ~]#
实验环境:
cluster1:192.168.177.100
cluster2:192.168.177.110
cluster3:192.168.177.111
cluster4:192.168.177.130
cluster5:192.168.177.120
cluster6:192.168.177.113
所有节点的配置一摸一样
vim /etc/redis/6379.conf
//定位70行,注释掉 bind 项,默认监听所有网卡
70 #bind 127.0.0.1
//定位89行,关闭保护模式
89 protected-mode no
//定位93行,redis默认端口6379 不用修改
93 port 6379
//定位137行,开启守护进程
137 daemonize yes
//定位700行,开启AOF持久化
700 appendonly yes
//定位833行,取消注释,开启群集功能
833 cluster-enabled yes
//定位841行,取消注释,群集名称文件设置
841 cluster-config-file nodes-6379.conf
//定位847行,取消注释群集超时时间设置
cluster-node-t imeout 15000
测试集群
[root@localhost ~]# redis-cli --cluster create 192.168.177.100:6379 192.168.177.110:6379 192.168.177.111:6379 192.168.177.130:6379 192.168.177.120:6379 192.168.177.113:6379 --cluster-replicas 1
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 192.168.177.120:6379 to 192.168.177.100:6379
Adding replica 192.168.177.113:6379 to 192.168.177.110:6379
Adding replica 192.168.177.130:6379 to 192.168.177.111:6379
M: cceb58f81f2081d2f9685a5c2c2f79df4b8e1b77 192.168.177.100:6379
slots:[0-5460] (5461 slots) master
M: 40726e20c1bb4273ab8b0573cd119b78811f8369 192.168.177.110:6379
slots:[5461-10922] (5462 slots) master
M: e4ded5db8151ddcfec57acdc97e86ebd7b135dd8 192.168.177.111:6379
slots:[10923-16383] (5461 slots) master
S: 70ab0718c44a43d8968f9e59fb3ef6a6c252b291 192.168.177.130:6379
replicates e4ded5db8151ddcfec57acdc97e86ebd7b135dd8
S: f03e81bd38e1ad27f55e91872e93191bf22e2063 192.168.177.120:6379
replicates cceb58f81f2081d2f9685a5c2c2f79df4b8e1b77
S: 0e2043f5f40ce9726d675db3503185364b010b05 192.168.177.113:6379
replicates 40726e20c1bb4273ab8b0573cd119b78811f8369
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 192.168.177.100:6379)
M: cceb58f81f2081d2f9685a5c2c2f79df4b8e1b77 192.168.177.100:6379
slots:[0-5460] (5461 slots) master
1 additional replica(s)
S: f03e81bd38e1ad27f55e91872e93191bf22e2063 192.168.177.120:6379
slots: (0 slots) slave
replicates cceb58f81f2081d2f9685a5c2c2f79df4b8e1b77
M: 40726e20c1bb4273ab8b0573cd119b78811f8369 192.168.177.110:6379
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
S: 70ab0718c44a43d8968f9e59fb3ef6a6c252b291 192.168.177.130:6379
slots: (0 slots) slave
replicates e4ded5db8151ddcfec57acdc97e86ebd7b135dd8
S: 0e2043f5f40ce9726d675db3503185364b010b05 192.168.177.113:6379
slots: (0 slots) slave
replicates 40726e20c1bb4273ab8b0573cd119b78811f8369
M: e4ded5db8151ddcfec57acdc97e86ebd7b135dd8 192.168.177.111:6379
slots:[10923-16383] (5461 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.
–cluster-replicas 1 六个实例分为三组,每组一主一从,前面的做主节点,后面的做从节点
使用 redis-cli cluster nodes 命令可以查看所有master和slave节点的信息,但是需要一一对应很长一串的id号来确定主从关系,很不方便,所以我使用了一个脚本(show_redis_map.sh)来显示所有主从的对应关系,脚本源码地址https://github.com/eyjian/redis-tools
使用方法: ./show_redis_map.sh 192.168.177.120:6379(随便一台节点的IP+端口)
[root@localhost ~]# redis-cli cluster nodes
f03e81bd38e1ad27f55e91872e93191bf22e2063 192.168.177.120:6379@16379 slave cceb58f81f2081d2f9685a5c2c2f79df4b8e1b77 0 1628276237604 5 connected
40726e20c1bb4273ab8b0573cd119b78811f8369 192.168.177.110:6379@16379 master - 0 1628276238664 2 connected 5461-10922
70ab0718c44a43d8968f9e59fb3ef6a6c252b291 192.168.177.130:6379@16379 slave e4ded5db8151ddcfec57acdc97e86ebd7b135dd8 0 1628276239731 4 connected
0e2043f5f40ce9726d675db3503185364b010b05 192.168.177.113:6379@16379 slave 40726e20c1bb4273ab8b0573cd119b78811f8369 0 1628276236530 6 connected
cceb58f81f2081d2f9685a5c2c2f79df4b8e1b77 192.168.177.100:6379@16379 myself,master - 0 0 1 connected 0-5460
e4ded5db8151ddcfec57acdc97e86ebd7b135dd8 192.168.177.111:6379@16379 master - 0 1628276240774 3 connected 10923-16383
[root@localhost ~]# ./show_redis_map.sh 192.168.177.130:6379
[01][MASTER] 192.168.177.100:6379 cceb58f81f2081d2f9685a5c2c2f79df4b8e1b77
[02][MASTER] 192.168.177.110:6379 40726e20c1bb4273ab8b0573cd119b78811f8369
[03][MASTER] 192.168.177.111:6379 e4ded5db8151ddcfec57acdc97e86ebd7b135dd8
[01][SLAVE=>MASTER] 192.168.177.113:6379 => 192.168.177.110:6379
[02][SLAVE=>MASTER] 192.168.177.120:6379 => 192.168.177.100:6379
[03][SLAVE=>MASTER] 192.168.177.130:6379 => 192.168.177.111:6379
[root@localhost ~]#
插入一个key-value 查看对应的哈希槽位置
[root@localhost ~]# redis-cli -c // -c 表示节点之间可以互相跳转
127.0.0.1:6379> cluster slots //查看哈希槽在各个节点的分布
1) 1) (integer) 5461
2) (integer) 10922
3) 1) "192.168.177.110"
2) (integer) 6379
3) "40726e20c1bb4273ab8b0573cd119b78811f8369"
4) 1) "192.168.177.113"
2) (integer) 6379
3) "0e2043f5f40ce9726d675db3503185364b010b05"
2) 1) (integer) 0
2) (integer) 5460
3) 1) "192.168.177.100"
2) (integer) 6379
3) "cceb58f81f2081d2f9685a5c2c2f79df4b8e1b77"
4) 1) "192.168.177.120"
2) (integer) 6379
3) "f03e81bd38e1ad27f55e91872e93191bf22e2063"
3) 1) (integer) 10923
2) (integer) 16383
3) 1) "192.168.177.111"
2) (integer) 6379
3) "e4ded5db8151ddcfec57acdc97e86ebd7b135dd8"
4) 1) "192.168.177.130"
2) (integer) 6379
3) "70ab0718c44a43d8968f9e59fb3ef6a6c252b291"
127.0.0.1:6379> set name zhangsan //插入一个string类型的key-value
-> Redirected to slot [5798] located at 192.168.177.110:6379 //对应的哈希槽在5798位置,对应的是192.168.177.110的master节点
OK
192.168.177.110:6379> get name
"zhangsan"
192.168.177.110:6379> cluster keyslot name //查看键对应的哈希槽的命令
(integer) 5798
192.168.177.110:6379>