背景
主从复制的两个作用:
- 从为主提供数据备份,当主挂掉的时候,从节点中有完整的数据可供恢复;
 
- 主从读写分离功能实现节点分流,将读操作放到从节点上执行,减轻主节点压力。
 
主从复制架构的痛点:
- 
手动故障转移
主从架构中如果主节点出现了问题,故障转移就需要手工完成,或者通过编写脚本来实现故障转移。
实现思路:当 master 故障后需要选出一个新的 slave 节点作为新的 master。
 
- 
写、存储能力受限
写能力和存储能力只能实现在主这一个节点上, 因为其他节点作为从节点都是主节点的副本。
解决方法:主从+分布式
 
手动故障迁移
主从复制-Master宕掉

上图模拟了一主两从。

假如主节点故障,那么从节点的复制也必然出现故障,连接 Master 的客户端读写势必也会出现问题。右侧从节点的读功能可能正常,但是数据的更新会受到影响。
Master故障后如何故障转移?

首先选择一个 Slave 作为新的 Master,在这个新的Master上执行 slaveof no one,让它成为一个 Master ,然后在其他的 Slave 执行 slaveof new master,也就是让其余节点去找新的 Master,这样就完成新建主从复制的过程,同时客户端也会去读写新的 Master。
上述过程也完全可以通过脚本来实现。需要让脚本监控master节点是否有问题,如果有问题就做节点下线操作,然后重新选择一个新的master,让其他slave都去复制这个新的master,最后去迁移客户端。
脚本的难点在于:
- 如何判断master节点有问题
 
- 迁移
 
- 怎么去通知客户端
 
- 整个过程如何去保证事务
 
纯脚本实现可能需要严谨的考虑,故Redis为我们提供了一个解决方案 — Redis Sentinel 高可用实现。
Redis Sentinel 架构

仍然是主从结构。另外还有多个 Redis Sentinel 节点,可以理解为多个Redis进程,但是这些进程不会去存储数据,它的作用是对Redis做故障判断和故障转移以及会通知客户端的过程。
**为何需要多个 Sentinel 节点?**因为这样可以保证对节点的判断是否故障具有公平性,另外还可以保证 Sentinel 节点的高可用,即使一个 Sentinel 挂了,也可以保证 Sentinel 的机制是完整的。
对于客户端来说它不会直接去从 Redis 来获取信息,也就是客户端不会直接记录 Redis 后端地址,比如某个IP和端口,而是记录Redis Sentinel 的地址。因为 Redis Sentinel 负责对所有 Redis 节点的监控,无论是master还是slave节点,Sentinel 知道谁是master,谁是slave,如果 Redis 节点故障 master改变了,Sentinel 是感知到的,客户端无需知道谁是master,而是由Sentinel来通知客户端。
所以客户端是不需要谁是真的master和slave,客户端只关心由sentinel来告诉客户端就行了。这样客户端知道后再去连接就可以了。
Redis Sentinel 的故障转移
1、redis 主节点故障之后,多个 Sentinel 发现并确认 master 有问题
2、然后 Sentinel 内部会进行选举,选出一个 sentinel 作为领导,因为重新选择master是要在一个节点上执行 slaveof no one,这个命令是在客户端执行的,让一个 sentinel 作为领导,可以理解为这个这个sentinel节点作为客户端,它会完成这样的功能。
3、接着选择一个slave 作为新的 master

4、然后通知其余salve称为新的master的slave。让新的slave去复制新的master

5、通知客户端主从变化。并且会通知客户端新的master是谁,让客户端不去连接老的故障master
6、如果期间等到老的master复活,则将其成为新的master的slave

7、另外sentinel还可以监控多套redis主从节点。这样可以有效节省资源。

每套redis主从节点会有个一个master-name的配置作为标识
Redis Sentinel 安装配置
1、配置开启主从节点
2、配置开启sentinel监控主节点(sentinel是特殊的redis节点)
特殊在于sentinel本身不存储数据,它支持的命令有限,它的作用主要是监控、故障转移和通知客户端
3、实际多台机器,这里在一台机器上通过端口区分
4、节点配置
结构如下:

主节点配置

配置文件均放在 config 配置目录下。
定义 redis-7000.conf 配置文件
1
2
3
  | 
# pwd
/opt/soft/redis/config
redis-7000.conf
  | 
 
配置如下:
1
2
3
4
5
  | 
port 7000
daemonize yes
pidfile /var/run/redis-7000.pid
logfile "7000.log"
dir "/opt/redis/data/"
  | 
 
从节点配置

1
2
3
4
  | 
# pwd
/opt/soft/redis/config
sed "s/7000/7001/g" redis-7000.conf > redis-7001.conf
sed "s/7000/7002/g" redis-7000.conf > redis-7002.conf
  | 
 
1
2
  | 
echo "slaveof 127.0.0.1 7000" >> redis-7001.conf
echo "slaveof 127.0.0.1 7000" >> redis-7002.conf
  | 
 
启动
1
2
3
4
5
6
  | 
# pwd
/opt/soft/redis/config
# redis-server redis-7000.conf
# redis-cli -p 7000 ping
PONG
#
  | 
 
1
2
3
4
5
6
  | 
# redis-server redis-7001.conf
# redis-server redis-7002.conf
# ps -ef | grep redis-server | grep 700
root      1600     1  2 08:13 ?        00:00:02 redis-server *:7000
root      1607     1  2 08:14 ?        00:00:00 redis-server *:7001
root      1612     1  3 08:14 ?        00:00:00 redis-server *:7002
  | 
 
查看主从复制关系:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
  | 
# redis-cli -p 7000 info replication
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=7001,state=online,offset=71,lag=0
slave1:ip=127.0.0.1,port=7002,state=online,offset=71,lag=0
master_repl_offset:71
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:70
  | 
 
sentinel 主要配置

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
  | 
# pwd
/opt/soft/redis/config
# cp ../sentinel.conf ./
# cat sentinel.conf | grep -v "#" | grep -v "^$"
port 26379
dir /tmp
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000
# cat sentinel.conf | grep -v "#" | grep -v "^$" > redis-sentinel-26379.conf
# vim redis-sentinel-26379.conf
  | 
 
配置修改如下:
1
2
3
4
5
6
7
8
  | 
port 26379
daemonize yes
dir /opt/soft/redis/data
logfile "26379.log"
sentinel monitor mymaster 127.0.0.1 7000 2
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000
  | 
 
启动:
1
2
3
4
5
  | 
# redis-sentinel redis-sentinel-26379.conf
# ps -ef | grep redis-sentinel
root      1652     1  5 08:27 ?        00:00:00 redis-sentinel *:26379 [sentinel]
root      1656  1556  0 08:27 pts/0    00:00:00 grep --color=auto redis-sentinel
#
  | 
 
连接sentinel:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
  | 
# redis-cli -p 26379
127.0.0.1:26379> set hello world
(error) ERR unknown command 'set'
127.0.0.1:26379> ping
PONG
127.0.0.1:26379> info
# Server
redis_version:3.0.7
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:196c6142377859a0
redis_mode:sentinel
os:Linux 3.10.0-1160.el7.x86_64 x86_64
arch_bits:64
multiplexing_api:epoll
gcc_version:4.8.5
process_id:1652
run_id:474751fcf93ad1302d5fec61a5764e2e888b280e
tcp_port:26379
uptime_in_seconds:72
uptime_in_days:0
hz:14
lru_clock:6962474
config_file:/opt/soft/redis-3.0.7/config/redis-sentinel-26379.conf
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
master0:name=mymaster,status=ok,address=127.0.0.1:7000,slaves=2,sentinels=1
127.0.0.1:26379> exit
  | 
 
从这句话“master0:name=mymaster,status=ok,address=127.0.0.1:7000,slaves=2,sentinels=1” 可以看到该sentinel监控到一个master,该master有两个从节点。
再次查看 redis-sentinel-26379.conf 配置:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
  | 
# cat redis-sentinel-26379.conf
port 26379
daemonize yes
dir "/opt/soft/redis-3.0.7/data"
logfile "26379.log"
sentinel monitor mymaster 127.0.0.1 7000 2
sentinel config-epoch mymaster 0
sentinel leader-epoch mymaster 0
sentinel known-slave mymaster 127.0.0.1 7002
# Generated by CONFIG REWRITE
sentinel known-slave mymaster 127.0.0.1 7001
sentinel current-epoch 0
  | 
 
发现配置最后多了一些内容,这是配置重写产生的,它发现了mymaster有两个slave,一个是7002,一个是7001,另外它将 “sentinel down-after-milliseconds mymaster 30000” 等最初的配置去掉了。
其余sentinel节点配置:
1
2
  | 
sed "s/26379/26380/g" redis-sentinel-26379.conf > redis-sentinel-26380.conf
sed "s/26379/26381/g" redis-sentinel-26379.conf > redis-sentinel-26381.conf
  | 
 
启动:
1
2
3
4
5
6
  | 
# redis-sentinel redis-sentinel-26380.conf
# redis-sentinel redis-sentinel-26381.conf
# ps -ef | grep redis-sentinel | grep -v grep
root      1652     1  4 08:27 ?        00:00:26 redis-sentinel *:26379 [sentinel]
root      1696     1  5 08:35 ?        00:00:01 redis-sentinel *:26380 [sentinel]
root      1700     1  5 08:35 ?        00:00:01 redis-sentinel *:26381 [sentinel]
  | 
 
连接新启动的sentinel节点:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
  | 
# redis-cli -p 26381
127.0.0.1:26381> info
# Server
redis_version:3.0.7
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:196c6142377859a0
redis_mode:sentinel
os:Linux 3.10.0-1160.el7.x86_64 x86_64
arch_bits:64
multiplexing_api:epoll
gcc_version:4.8.5
process_id:1700
run_id:875718eefbfae06b495be5e041033885321e9d5d
tcp_port:26381
uptime_in_seconds:209
uptime_in_days:0
hz:14
lru_clock:6963119
config_file:/opt/soft/redis-3.0.7/config/redis-sentinel-26381.conf
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
master0:name=mymaster,status=ok,address=127.0.0.1:7000,slaves=2,sentinels=3
  | 
 
sentinels=3 表示发现了3个sentinel,sentinel 节点之间是可以感知到的,比如在看下 26380 节点的info:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
  | 
127.0.0.1:26381> exit
[root@localhost config]# redis-cli -p 26380
127.0.0.1:26380> info
# Server
redis_version:3.0.7
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:196c6142377859a0
redis_mode:sentinel
os:Linux 3.10.0-1160.el7.x86_64 x86_64
arch_bits:64
multiplexing_api:epoll
gcc_version:4.8.5
process_id:1696
run_id:18af496186a7cf64e6cb30a1c3e4d97f3c9438ee
tcp_port:26380
uptime_in_seconds:292
uptime_in_days:0
hz:11
lru_clock:6963199
config_file:/opt/soft/redis-3.0.7/config/redis-sentinel-26380.conf
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
master0:name=mymaster,status=ok,address=127.0.0.1:7000,slaves=2,sentinels=3
127.0.0.1:26380>
  | 
 
故障转移演练
1、客户端高可用观察
2、服务端日志分析:数据节点和sentinel节点