Redis的主从复制功能非常强大,一个master可以拥有多个slave,而一个slave又可以拥有多个slave,如此下去,形成了强大的多级服务器集群架构。
官网:https://redis.io/

  • 环境:
    Master:
[root@Master ~]# uname -a
Linux Master.Redis 3.10.0-514.el7.x86_64 #1 SMP Tue Nov 22 16:42:41 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
[root@Master ~]# 

Slave

[root@Slave ~]# uname -a
Linux Slave.Redis 3.10.0-327.el7.x86_64 #1 SMP Thu Nov 19 22:10:57 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux
[root@Slave ~]# 

实验中Slave上启动了2个实例,port1:6979 port2:6980

Redis
redis-4.0.10

Redis主从复制(读写分离)配置

Redis主从复制可以根据是否是全量分为全量同步和增量同步:
1、全量同步
Redis全量复制一般发生在Slave初始化阶段,这时Slave需要将Master上的所有数据都复制一份。具体步骤如下:
  1)从服务器连接主服务器,发送SYNC命令;
  2)主服务器接收到SYNC命名后,开始执行BGSAVE命令生成RDB文件并使用缓冲区记录此后执行的所有写命令;
  3)主服务器BGSAVE执行完后,向所有从服务器发送快照文件,并在发送期间继续记录被执行的写命令;
  4)从服务器收到快照文件后丢弃所有旧数据,载入收到的快照;
  5)主服务器快照发送完毕后开始向从服务器发送缓冲区中的写命令;
  6)从服务器完成对快照的载入,开始接收命令请求,并执行来自主服务器缓冲区的写命令;
Redis主从复制(读写分离)、哨兵(主从切换)配置
完成上面几个步骤后就完成了从服务器数据初始化的所有操作,从服务器此时可以接收来自用户的读请求。

2 增量同步
Redis增量复制是指Slave初始化后开始正常工作时主服务器发生的写操作同步到从服务器的过程。 增量复制的过程主要是主服务器每执行一个写命令就会向从服务器发送相同的写命令,从服务器接收并执行收到的写命令。

3、Redis主从同步策略
主从刚刚连接的时候,进行全量同步;全同步结束后,进行增量同步。当然,如果有需要,slave 在任何时候都可以发起全量同步。redis 策略是,无论如何,首先会尝试进行增量同步,如不成功,要求从机进行全量同步。

  • 安装

1、redis主不需要特别配置,按照正常配置即可
2、redis从,需要在配置文件里指定redis主

Master

[root@Master ~]# mkdir /opt/soft
[root@Master ~]# cd /opt/soft/
[root@Master soft]#  wge thttp://download.redis.io/releases/redis-4.0.10.tar.gz
[root@Master soft]# tar -zxvf redis-4.0.10.tar.gz
[root@Master soft]# cd redis-4.0.10
[root@Master redis-4.0.10]# make
......
Hint: It's a good idea to run 'make test' ;)

make[1]: Leaving directory `/opt/soft/redis-4.0.10/src'
[root@Master redis-4.0.10]# cd src/
[root@Master src]# make test
......

\o/ All tests passed without errors!

Cleanup: may take some time... OK
[root@Master src]# mkdir -p /opt/redis/{logs,etc,data,bin}
[root@Master src]# vim /opt/redis/etc/redis.conf
[root@Master src]# cat /opt/redis/etc/redis.conf |grep -v "#"|sed '/^[[:space:]]*$/d'
bind 0.0.0.0
protected-mode yes
port 6979
tcp-backlog 511
timeout 0
tcp-keepalive 300
daemonize yes
supervised no
pidfile /var/run/redis_6979.pid
loglevel notice
logfile "./logs/redis.log"
databases 16
always-show-logo yes
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
dir ./data/
slave-serve-stale-data yes
slave-read-only yes
repl-diskless-sync no
repl-diskless-sync-delay 5
repl-disable-tcp-nodelay no
slave-priority 100
requirepass 51cto
lazyfree-lazy-eviction no
lazyfree-lazy-expire no
lazyfree-lazy-server-del no
slave-lazy-flush no
appendonly no
appendfilename "appendonly.aof"
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes
aof-use-rdb-preamble no
lua-time-limit 5000
slowlog-log-slower-than 10000
slowlog-max-len 128
latency-monitor-threshold 0
notify-keyspace-events ""
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-size -2
list-compress-depth 0
set-max-intset-entries 512
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
hll-sparse-max-bytes 3000
activerehashing yes
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit slave 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
hz 10
aof-rewrite-incremental-fsync yes
[root@Master src]# touch /opt/redis/logs/redis.log
[root@Master src]# echo 'vm.overcommit_memory = 1' >> /etc/sysctl.conf
[root@Master src]#  sysctl -p
[root@Master src]# echo '511' > /proc/sys/net/core/somaxconn
[root@Master src]# echo 'never' > /sys/kernel/mm/transparent_hugepage/enabled 
[root@Master src]# cd /opt/redis/bin/
[root@Master bin]# ./redis-server ../etc/redis.conf 
[root@Master bin]# tail -500f ../logs/redis.log 
9084:C 30 Jun 23:16:40.766 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
9084:C 30 Jun 23:16:40.766 # Redis version=4.0.10, bits=64, commit=00000000, modified=0, pid=9084, just started
9084:C 30 Jun 23:16:40.766 # Configuration loaded
                _._                                                  
           _.-``__ ''-._                                             
      _.-``    `.  `_.  ''-._           Redis 4.0.10 (00000000/0) 64 bit
  .-`` .-```.  ```\/    _.,_ ''-._                                   
 (    '      ,       .-`  | `,    )     Running in standalone mode
 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 6979
 |    `-._   `._    /     _.-'    |     PID: 9085
  `-._    `-._  `-./  _.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |           http://redis.io        
  `-._    `-._`-.__.-'_.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |                                  
  `-._    `-._`-.__.-'_.-'    _.-'                                   
      `-._    `-.__.-'    _.-'                                       
          `-._        _.-'                                           
              `-.__.-'                                               

9085:M 30 Jun 23:16:40.770 # Server initialized
9085:M 30 Jun 23:16:40.771 * Ready to accept connections

Slave安装完以后将Master上的/opt/redis整个文件夹都拷贝过去

[root@Master bin]# scp -r /opt/redis root@10.15.43.16:/opt/

修改Slave上redis配置

[root@Slave redis]# cat /opt/redis/etc/redis_6979.conf |grep -v "#"|sed '/^[[:space:]]*$/d'
bind 0.0.0.0
protected-mode yes
port 6979
tcp-backlog 511
timeout 0
tcp-keepalive 300
daemonize yes
supervised no
pidfile /var/run/redis_6979.pid
loglevel notice
logfile "../logs/redis_6979.log"
databases 16
always-show-logo yes
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump_6979.rdb
dir ../data/
slaveof 10.15.43.15 6979   //master地址 端口
masterauth 51cto      //master的密码,如果设置了需要开启此项
slave-serve-stale-data yes
slave-read-only yes
repl-diskless-sync no
repl-diskless-sync-delay 5
repl-disable-tcp-nodelay no
slave-priority 100        #slave的优先级,是一个整数,展示在Redis的Info输出中。如果master不再正常工作了,哨兵将这一个slave提升为master。
# 优先级数字小的salve会优先考虑提升为master,所以例如有三个slave优先级分别为10,100,25,哨兵将挑选优先级最小数字为10的slave。
# 0作为一个特殊的优先级,标识这个slave不能作为master,所以一个优先级为0的slave永远不会被哨兵挑选提升为master
requirepass 51cto
lazyfree-lazy-eviction no
lazyfree-lazy-expire no
lazyfree-lazy-server-del no
slave-lazy-flush no
appendonly no
appendfilename "appendonly.aof"
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes
aof-use-rdb-preamble no
lua-time-limit 5000
slowlog-log-slower-than 10000
slowlog-max-len 128
latency-monitor-threshold 0
notify-keyspace-events ""
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-size -2
list-compress-depth 0
set-max-intset-entries 512
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
hll-sparse-max-bytes 3000
activerehashing yes
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit slave 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
hz 10
aof-rewrite-incremental-fsync yes
[root@Slave redis]# echo 'vm.overcommit_memory = 1' >> /etc/sysctl.conf
[root@Slave redis]# sysctl -p
[root@Slave redis]# echo '511' > /proc/sys/net/core/somaxconn
[root@Slave redis]# echo 'never' > /sys/kernel/mm/transparent_hugepage/enabled
[root@Slave redis]# cd bin/
[root@Slave bin]# ./redis-server ../etc/redis.conf 
[root@Slave bin]# tail -500f ../logs/redis.log 
9025:C 30 Jun 23:31:06.487 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
9025:C 30 Jun 23:31:06.487 # Redis version=4.0.10, bits=64, commit=00000000, modified=0, pid=9025, just started
9025:C 30 Jun 23:31:06.487 # Configuration loaded
                _._                                                  
           _.-``__ ''-._                                             
      _.-``    `.  `_.  ''-._           Redis 4.0.10 (00000000/0) 64 bit
  .-`` .-```.  ```\/    _.,_ ''-._                                   
 (    '      ,       .-`  | `,    )     Running in standalone mode
 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 6979
 |    `-._   `._    /     _.-'    |     PID: 9026
  `-._    `-._  `-./  _.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |           http://redis.io        
  `-._    `-._`-.__.-'_.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |                                  
  `-._    `-._`-.__.-'_.-'    _.-'                                   
      `-._    `-.__.-'    _.-'                                       
          `-._        _.-'                                           
              `-.__.-'                                               

9026:S 30 Jun 23:31:06.491 # Server initialized
9026:S 30 Jun 23:31:06.491 * DB loaded from disk: 0.000 seconds
9026:S 30 Jun 23:31:06.491 * Before turning into a slave, using my master parameters to synthesize a cached master: I may be able to synchronize with the new master with just a partial transfer.
9026:S 30 Jun 23:31:06.491 * Ready to accept connections
9026:S 30 Jun 23:31:06.491 * Connecting to MASTER 10.15.43.15:6979
9026:S 30 Jun 23:31:06.492 * MASTER <-> SLAVE sync started
9026:S 30 Jun 23:31:06.492 * Non blocking connect for SYNC fired the event.
9026:S 30 Jun 23:31:06.492 * Master replied to PING, replication can continue...
9026:S 30 Jun 23:31:06.494 * Trying a partial resynchronization (request 88f6e9e624a3f8af365809c46e8a3ee2f16945b7:1).
9026:S 30 Jun 23:31:06.494 * Successful partial resynchronization with master.
9026:S 30 Jun 23:31:06.494 * MASTER <-> SLAVE sync: Master accepted a Partial Resynchronization.

在Slave机器上再起一个实例,复制上面从的配置文件,并修改端口为6980

[root@Slave bin]# cp ../etc/redis_6979.conf ../etc/redis_6980.conf 
[root@Slave bin]# sed -i 's/6979/6980/g' ../etc/redis_6980.conf
[root@Slave bin]# touch ../logs/redis_6980.log
[root@Slave bin]# ./redis-server ../etc/redis_6980.conf
[root@Slave bin]# tail -500f ../logs/redis_6980.log
1974:C 01 Jul 11:12:09.351 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
1974:C 01 Jul 11:12:09.351 # Redis version=4.0.10, bits=64, commit=00000000, modified=0, pid=1974, just started
1974:C 01 Jul 11:12:09.351 # Configuration loaded
                _._                                                  
           _.-``__ ''-._                                             
      _.-``    `.  `_.  ''-._           Redis 4.0.10 (00000000/0) 64 bit
  .-`` .-```.  ```\/    _.,_ ''-._                                   
 (    '      ,       .-`  | `,    )     Running in standalone mode
 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 6980
 |    `-._   `._    /     _.-'    |     PID: 1975
  `-._    `-._  `-./  _.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |           http://redis.io        
  `-._    `-._`-.__.-'_.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |                                  
  `-._    `-._`-.__.-'_.-'    _.-'                                   
      `-._    `-.__.-'    _.-'                                       
          `-._        _.-'                                           
              `-.__.-'                                               

1975:S 01 Jul 11:12:09.355 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
1975:S 01 Jul 11:12:09.355 # Server initialized
1975:S 01 Jul 11:12:09.355 * Ready to accept connections
1975:S 01 Jul 11:12:09.355 * Connecting to MASTER 10.15.43.15:6979
1975:S 01 Jul 11:12:09.355 * MASTER <-> SLAVE sync started
1975:S 01 Jul 11:12:09.356 * Non blocking connect for SYNC fired the event.
1975:S 01 Jul 11:12:09.356 * Master replied to PING, replication can continue...
1975:S 01 Jul 11:12:09.358 * Partial resynchronization not possible (no cached master)
1975:S 01 Jul 11:12:09.359 * Full resync from master: 88f6e9e624a3f8af365809c46e8a3ee2f16945b7:57196
1975:S 01 Jul 11:12:09.365 * MASTER <-> SLAVE sync: receiving 214 bytes from master
1975:S 01 Jul 11:12:09.366 * MASTER <-> SLAVE sync: Flushing old data
1975:S 01 Jul 11:12:09.366 * MASTER <-> SLAVE sync: Loading DB in memory
1975:S 01 Jul 11:12:09.366 * MASTER <-> SLAVE sync: Finished with success
  • 主从验证
[root@Slave bin]# ./redis-cli -h 10.15.43.15 -p 6979 -a 51cto info replication
Warning: Using a password with '-a' option on the command line interface may not be safe.
# Replication
role:master
connected_slaves:2
slave0:ip=10.15.43.16,port=6979,state=online,offset=187508,lag=0
slave1:ip=10.15.43.16,port=6980,state=online,offset=187508,lag=1
master_replid:88f6e9e624a3f8af365809c46e8a3ee2f16945b7
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:187508
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:187508
[root@Slave bin]# ./redis-cli -h 10.15.43.16 -p 6979 -a 51cto info replication
Warning: Using a password with '-a' option on the command line interface may not be safe.
# Replication
role:slave
master_host:10.15.43.15
master_port:6979
master_link_status:up
master_last_io_seconds_ago:0
master_sync_in_progress:0
slave_repl_offset:187550
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:88f6e9e624a3f8af365809c46e8a3ee2f16945b7
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:187550
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:56987
repl_backlog_histlen:130564
[root@Slave bin]# ./redis-cli -h 10.15.43.16 -p 6980 -a 51cto info replication
Warning: Using a password with '-a' option on the command line interface may not be safe.
# Replication
role:slave
master_host:10.15.43.15
master_port:6979
master_link_status:up
master_last_io_seconds_ago:3
master_sync_in_progress:0
slave_repl_offset:187564
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:88f6e9e624a3f8af365809c46e8a3ee2f16945b7
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:187564
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:57197
repl_backlog_histlen:130368
[root@Slave bin]# ./redis-cli -h 10.15.43.15 -p 6979 -a 51cto
Warning: Using a password with '-a' option on the command line interface may not be safe.
10.15.43.15:6979> set justin "51cto id:ityunwei2017"
OK
10.15.43.15:6979> get justin
"51cto id:ityunwei2017"
10.15.43.15:6979> quit
[root@Slave bin]# ./redis-cli -h 10.15.43.16 -p 6979 -a 51cto
Warning: Using a password with '-a' option on the command line interface may not be safe.
10.15.43.16:6979> get justin
"51cto id:ityunwei2017"
10.15.43.16:6979> del justin
(error) READONLY You can't write against a read only slave.
10.15.43.16:6979> quit
[root@Slave bin]# ./redis-cli -h 10.15.43.16 -p 6980 -a 51cto
Warning: Using a password with '-a' option on the command line interface may not be safe.
10.15.43.16:6980> get justin
"51cto id:ityunwei2017"
10.15.43.16:6980> del justin
(error) READONLY You can't write against a read only slave.
10.15.43.16:6980> quit
[root@Slave bin]# 

在master上插入键值数据后在slave上可以获取到,主从同步正常,slave上只能查看,不能进行写操作。

哨兵(主从切换)配置

Redis官方提供了一个工具sentinel(哨兵),sentinel在下载的redis源码里。
Redis主从复制(读写分离)、哨兵(主从切换)配置

  • sentinel系统会执行以下3个任务:
    1、监控(Monitoring):不断的检查你的主服务器和从服务器是否运行正常;
    2、通知(Notification):当被监控的某个redis服务器出现问题时,Sentinel可以通过API向管理员或者其他应用程序发送通知;
    3、自动故障迁移(Automatic failover):当一个主服务器不能正常工作时,sentinel会开始一次自动故障迁移操作,它会将失效的主服务器的其中一个从服务器升级为新的主服务器,并让失效主服务器的其他从服务器为复制新的主服务器。当客户端试图连接失效的主服务器时,集群也会向客户端返回新主服务器地址,使得集群可以使用新主服务器代替失效服务器。

  • sentinel的配置
[root@Slave bin]# cp /opt/soft/redis-4.0.10/sentinel.conf ./etc/
[root@Slave bin]# cp /opt/soft/redis-4.0.10/sentinel.conf ./etc/
[root@Slave bin]# mkdir /opt/redis/tmp
[root@Slave bin]# grep -v "#" ../etc/sentinel.conf |sed '/^[[:space:]]*$/d'
port 26979
dir ../tmp
sentinel monitor master15 10.15.43.15 6979 1
sentinel auth-pass master15 51cto
sentinel down-after-milliseconds master15 30000
sentinel parallel-syncs master15 1
sentinel failover-timeout master15 180000
logfile ../logs/sentinel.log
[root@Slave bin]#

master15表示要监控的主库的名字,可以自己定义。这个名字必须仅由大小写字母、数字和”.-”这3个字符组成。后两个参数表示主库的IP地址和端口号。最后的1表示最低通过票数,这里为了试验只使用了一个sentinel,实际坏境中建议使用2n+1个sentinel。

[root@Slave bin]# nohup ./redis-sentinel ../etc/sentinel.conf --sentinel &
[1] 3193
[root@Slave bin]# nohup: ignoring input and appending output to ‘nohup.out’
[root@Slave bin]# ./redis-cli -p 26979 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=master15,status=ok,address=10.15.43.15:6979,slaves=2,sentinels=1
[root@Slave bin]# tail -500f ../logs/sentinel.log 
3193:X 02 Jul 14:04:46.745 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
3193:X 02 Jul 14:04:46.745 # Redis version=4.0.10, bits=64, commit=00000000, modified=0, pid=3193, just started
3193:X 02 Jul 14:04:46.745 # Configuration loaded
3193:X 02 Jul 14:04:46.747 * Running mode=sentinel, port=26979.
3193:X 02 Jul 14:04:46.747 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
3193:X 02 Jul 14:04:46.749 # Sentinel ID is 2d38409d6a2c8ffadd899180eb874afb17486c2c
3193:X 02 Jul 14:04:46.749 # +monitor master master15 10.15.43.15 6979 quorum 1
3193:X 02 Jul 14:04:46.750 * +slave slave 10.15.43.16:6979 10.15.43.16 6979 @ master15 10.15.43.15 6979
3193:X 02 Jul 14:04:46.751 * +slave slave 10.15.43.16:6980 10.15.43.16 6980 @ master15 10.15.43.15 6979

其中”+slave”表示新发现了从库,可见哨兵成功地发现了两个从库:10.15.43.16:6979、10.15.43.16:6980

这时候sentinel.conf文件被修改了

[root@Slave bin]# grep -v "#" ../etc/sentinel.conf |sed '/^[[:space:]]*$/d'
port 26979
dir "/opt/redis/tmp"
sentinel myid 2d38409d6a2c8ffadd899180eb874afb17486c2c
sentinel monitor master15 10.15.43.15 6979 1
sentinel auth-pass master15 51cto
sentinel config-epoch master15 0
sentinel leader-epoch master15 0
logfile "../logs/sentinel.log"
sentinel known-slave master15 10.15.43.16 6980
sentinel known-slave master15 10.15.43.16 6979
sentinel current-epoch 0
[root@Slave bin]# 

现在哨兵已经在监控这3个Redis实例,这时将主库关闭(杀死进程或使用 shutdown 命令),等待指定时间后(down-after-milliseconds,默认为 30 秒),哨兵会输出如下内容:

[root@Slave bin]# tail -500f ../logs/sentinel.log 
3193:X 02 Jul 14:20:20.439 # +sdown master master15 10.15.43.15 6979
3193:X 02 Jul 14:20:20.439 # +odown master master15 10.15.43.15 6979 #quorum 1/1
3193:X 02 Jul 14:20:20.439 # +new-epoch 1
3193:X 02 Jul 14:20:20.439 # +try-failover master master15 10.15.43.15 6979
3193:X 02 Jul 14:20:20.441 # +vote-for-leader 2d38409d6a2c8ffadd899180eb874afb17486c2c 1
3193:X 02 Jul 14:20:20.441 # +elected-leader master master15 10.15.43.15 6979
3193:X 02 Jul 14:20:20.441 # +failover-state-select-slave master master15 10.15.43.15 6979
3193:X 02 Jul 14:20:20.531 # +selected-slave slave 10.15.43.16:6979 10.15.43.16 6979 @ master15 10.15.43.15 6979
3193:X 02 Jul 14:20:20.532 * +failover-state-send-slaveof-noone slave 10.15.43.16:6979 10.15.43.16 6979 @ master15 10.15.43.15 6979
3193:X 02 Jul 14:20:20.615 * +failover-state-wait-promotion slave 10.15.43.16:6979 10.15.43.16 6979 @ master15 10.15.43.15 6979
3193:X 02 Jul 14:20:20.622 # +promoted-slave slave 10.15.43.16:6979 10.15.43.16 6979 @ master15 10.15.43.15 6979
3193:X 02 Jul 14:20:20.622 # +failover-state-reconf-slaves master master15 10.15.43.15 6979
3193:X 02 Jul 14:20:20.692 * +slave-reconf-sent slave 10.15.43.16:6980 10.15.43.16 6980 @ master15 10.15.43.15 6979
3193:X 02 Jul 14:20:21.283 * +slave-reconf-inprog slave 10.15.43.16:6980 10.15.43.16 6980 @ master15 10.15.43.15 6979
3193:X 02 Jul 14:20:22.331 * +slave-reconf-done slave 10.15.43.16:6980 10.15.43.16 6980 @ master15 10.15.43.15 6979
3193:X 02 Jul 14:20:22.394 # +failover-end master master15 10.15.43.15 6979
3193:X 02 Jul 14:20:22.394 # +switch-master master15 10.15.43.15 6979 10.15.43.16 6979
3193:X 02 Jul 14:20:22.394 * +slave slave 10.15.43.16:6980 10.15.43.16 6980 @ master15 10.15.43.16 6979
3193:X 02 Jul 14:20:22.394 * +slave slave 10.15.43.15:6979 10.15.43.15 6979 @ master15 10.15.43.16 6979
3193:X 02 Jul 14:20:52.440 # +sdown slave 10.15.43.15:6979 10.15.43.15 6979 @ master15 10.15.43.16 6979

+sdown,表示哨兵主观认为主库停止服务了(Subjectively Down, 简称 SDOWN)指的是单个 Sentinel 实例对服务器做出的下线判断
+odown,表示哨兵客观认为主库停止服务了(Objectively Down, 简称 ODOWN)指的是多个 Sentinel 实例在对同一个服务器做出 SDOWN 判断, 并且通过SENTINEL is-master-down-by-addr 命令互相交流之后, 得出的服务器下线判断。 (一个 Sentinel 可以通过向另一个 Sentinel 发送 SENTINEL is-master-down-by-addr 命令来询问对方是否认为给定的服务器已下线。)
+try-failover,表示哨兵开始进行故障恢复
+failover-end,表示哨兵完成故障恢复,期间涉及包括领头哨兵的选举、备选从库的选择等,
+switch-master,表示开始发送切换master指令了,主库从10.15.43.15 6979迁移到10.15.43.16 6979,
+slave,列出了新主库的两个从库,10.15.43.16:6980、10.15.43.15:6979
+sdown,这里kill掉了原来的主库,哨兵主观认为主库停止服务了

哨兵并没有彻底清除停止服务实例的信息,这是因为停止服务的实例可能会在之后的某个时间恢复服务,这时哨兵会让其重新加入进来,所以当实例停止服务后,哨兵会更新该实例的信息,使得当其重新加入后可以按照当前信息继续对外提供服务。此例中10.15.43.15:6979的主库实例停止服务了,而10.15.43.16 6979的从库已经升级为主库,当6379端口的实例恢复服务后,会转变为6381端口实例的从库来运行,所以哨兵将6379端口实例的信息修改成了 6381端口实例的从库。

[root@Slave bin]# ./redis-cli -p 26979 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=master15,status=ok,address=10.15.43.16:6979,slaves=2,sentinels=1
[root@Slave bin]# ./redis-cli -p 6979 -a 51cto info replication
Warning: Using a password with '-a' option on the command line interface may not be safe.
# Replication
role:master
connected_slaves:1
slave0:ip=10.15.43.16,port=6980,state=online,offset=371290,lag=1
master_replid:9f381205761bc87469d017aa8cdf927154861d4a
master_replid2:88f6e9e624a3f8af365809c46e8a3ee2f16945b7
master_repl_offset:371290
second_repl_offset:262772
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:56987
repl_backlog_histlen:314304
[root@Slave bin]# ./redis-cli -p 6980 -a 51cto info replication
Warning: Using a password with '-a' option on the command line interface may not be safe.
# Replication
role:slave
master_host:10.15.43.16
master_port:6979
master_link_status:up
master_last_io_seconds_ago:1
master_sync_in_progress:0
slave_repl_offset:371852
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:9f381205761bc87469d017aa8cdf927154861d4a
master_replid2:88f6e9e624a3f8af365809c46e8a3ee2f16945b7
master_repl_offset:371852
second_repl_offset:262772
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:57197
repl_backlog_histlen:314656
[root@Slave bin]# 

此时,重新启动10.15.43.15 6979实例,查看sentinel日志

[root@Slave bin]# tail -500f ../logs/sentinel.log 
3193:X 02 Jul 14:55:28.440 # -sdown slave 10.15.43.15:6979 10.15.43.15 6979 @ master15 10.15.43.16 6979
3193:X 02 Jul 14:55:38.398 * +convert-to-slave slave 10.15.43.15:6979 10.15.43.15 6979 @ master15 10.15.43.16 6979

“-sdown”,表示实例10.15.43.15 6979已经恢复了服务(与+sdown相反),
”+convert-to-slave”,表示将10.15.43.15 6979端口的实例设置为10.15.43.16 6979实例的从库。

查看10.15.43.15的复制信息

[root@Slave bin]# ./redis-cli -h 10.15.43.15 -p 6979 -a 51cto info replication
Warning: Using a password with '-a' option on the command line interface may not be safe.
# Replication
role:slave
master_host:10.15.43.16
master_port:6979
master_link_status:down
master_last_io_seconds_ago:-1
master_sync_in_progress:0
slave_repl_offset:1
master_link_down_since_seconds:1530514926
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:312b860d03bc08ac37976005df1d8c6fc1378038
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
[root@Slave bin]# 

10.15.43.15 6979 限制是slave,但是连接master失败,这是因为现在的master的设置了密码,需要在配置文件里加上masterauth "51cto",为了能主动切换成功过在master设置了密码是也需要在master的配置文件里加上masterauth "51cto"配置好项,修改后重启服务

[root@Slave bin]# ./redis-cli -h 10.15.43.15 -p 6979 -a 51cto info replication
Warning: Using a password with '-a' option on the command line interface may not be safe.
# Replication
role:slave
master_host:10.15.43.16
master_port:6979
master_link_status:up
master_last_io_seconds_ago:1
master_sync_in_progress:0
slave_repl_offset:454348
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:9f381205761bc87469d017aa8cdf927154861d4a
master_replid2:88f6e9e624a3f8af365809c46e8a3ee2f16945b7
master_repl_offset:454348
second_repl_offset:57197
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:57197
repl_backlog_histlen:397152
[root@Slave bin]# ./redis-cli -p 6979 -a 51cto info replication
Warning: Using a password with '-a' option on the command line interface may not be safe.
# Replication
role:master
connected_slaves:2
slave0:ip=10.15.43.16,port=6980,state=online,offset=457432,lag=1
slave1:ip=10.15.43.15,port=6979,state=online,offset=457432,lag=0
master_replid:9f381205761bc87469d017aa8cdf927154861d4a
master_replid2:88f6e9e624a3f8af365809c46e8a3ee2f16945b7
master_repl_offset:457432
second_repl_offset:262772
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:56987
repl_backlog_histlen:400446
[root@Slave bin]#

如果此时,主10.15.43.16 6979宕机,在一般情况下,lag的值应该在0秒或者1秒之间跳动,如果超过1秒的话,那么说明主从服务器之间的连接出现了故障。

[root@Slave bin]# tail -500f ../logs/sentinel.log
3193:X 02 Jul 15:09:35.979 # +sdown master master15 10.15.43.16 6979
3193:X 02 Jul 15:09:35.980 # +odown master master15 10.15.43.16 6979 #quorum 1/1
3193:X 02 Jul 15:09:35.980 # +new-epoch 2
3193:X 02 Jul 15:09:35.980 # +try-failover master master15 10.15.43.16 6979
3193:X 02 Jul 15:09:35.983 # +vote-for-leader 2d38409d6a2c8ffadd899180eb874afb17486c2c 2
3193:X 02 Jul 15:09:35.983 # +elected-leader master master15 10.15.43.16 6979
3193:X 02 Jul 15:09:35.983 # +failover-state-select-slave master master15 10.15.43.16 6979
3193:X 02 Jul 15:09:36.075 # +selected-slave slave 10.15.43.15:6979 10.15.43.15 6979 @ master15 10.15.43.16 6979
3193:X 02 Jul 15:09:36.075 * +failover-state-send-slaveof-noone slave 10.15.43.15:6979 10.15.43.15 6979 @ master15 10.15.43.16 6979
3193:X 02 Jul 15:09:36.131 * +failover-state-wait-promotion slave 10.15.43.15:6979 10.15.43.15 6979 @ master15 10.15.43.16 6979
3193:X 02 Jul 15:09:36.858 # +promoted-slave slave 10.15.43.15:6979 10.15.43.15 6979 @ master15 10.15.43.16 6979
3193:X 02 Jul 15:09:36.858 # +failover-state-reconf-slaves master master15 10.15.43.16 6979
3193:X 02 Jul 15:09:36.916 * +slave-reconf-sent slave 10.15.43.16:6980 10.15.43.16 6980 @ master15 10.15.43.16 6979
3193:X 02 Jul 15:09:37.879 * +slave-reconf-inprog slave 10.15.43.16:6980 10.15.43.16 6980 @ master15 10.15.43.16 6979
3193:X 02 Jul 15:09:37.879 * +slave-reconf-done slave 10.15.43.16:6980 10.15.43.16 6980 @ master15 10.15.43.16 6979
3193:X 02 Jul 15:09:37.961 # +failover-end master master15 10.15.43.16 6979
3193:X 02 Jul 15:09:37.962 # +switch-master master15 10.15.43.16 6979 10.15.43.15 6979
3193:X 02 Jul 15:09:37.962 * +slave slave 10.15.43.16:6980 10.15.43.16 6980 @ master15 10.15.43.15 6979
3193:X 02 Jul 15:09:37.962 * +slave slave 10.15.43.16:6979 10.15.43.16 6979 @ master15 10.15.43.15 6979
3193:X 02 Jul 15:10:07.982 # +sdown slave 10.15.43.16:6979 10.15.43.16 6979 @ master15 10.15.43.15 6979

主自动切换到10.15.43.15 6979

[root@Slave bin]# ./redis-cli -p 26979 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=master15,status=ok,address=10.15.43.15:6979,slaves=2,sentinels=1
[root@Slave bin]# ./redis-cli -p 6980 -a 51cto info replication
Warning: Using a password with '-a' option on the command line interface may not be safe.
# Replication
role:slave
master_host:10.15.43.15
master_port:6979
master_link_status:up
master_last_io_seconds_ago:1
master_sync_in_progress:0
slave_repl_offset:472682
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:d92482767f5f83abc3aacb8057081c01a192c122
master_replid2:9f381205761bc87469d017aa8cdf927154861d4a
master_repl_offset:472682
second_repl_offset:463436
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:57197
repl_backlog_histlen:415486
[root@Slave bin]# ./redis-cli -h 10.15.43.15 -p 6979 -a 51cto info replication
Warning: Using a password with '-a' option on the command line interface may not be safe.
# Replication
role:master
connected_slaves:1
slave0:ip=10.15.43.16,port=6980,state=online,offset=473381,lag=1
master_replid:d92482767f5f83abc3aacb8057081c01a192c122
master_replid2:9f381205761bc87469d017aa8cdf927154861d4a
master_repl_offset:473518
second_repl_offset:463436
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:57197
repl_backlog_histlen:416322
[root@Slave bin]# 

哨兵进程启动时读取配置文件的内容,通过sentinel monitor master-name ip redis-port quorum项找出需要监控的主库, master-name 是主库的名字,因为考虑到故障恢复后当前监控的系统的主库的地址和端口会产生变化,所以哨兵提供了命令可以通过主库的名字获取当前系统的主库的IP地址和端口号。
一个哨兵节点可以同时监控多个Redis主从系统,只需要定义每个主库的名字”sentinel monitor”配置即可,一个哨兵节点可以同时监控多个Redis主从系统,只需要提供多个”sentinel monitor”配置即可。snetinel的状态会被持久化地写入sentinel的配置文件中。每次当收到一个新的配置时,或者新创建一个配置时,配置会被持久化到硬盘中,并带上配置的版本戳,这样就可以安全的停止和重启sentinel进程。

  • 哨兵启动后,会与要监控的主库建立两条连接:

1、一条用来订阅该主库的”sentinel:hello”频道,以获取其他同样监控该数据库的哨兵节点的信息。
2、一条定期向主库发送info等命令来获取主库本身的信息,由于进入订阅模式时就不能再执行其他命令了,所以这时哨兵会使用另外一条连接来发送这些命令。

  • 和主库的连接建立完成后,哨兵会定时执行3个操作:

    1、每10秒向主库和从库发送info命令
    发送info命令使哨兵可以获得当前数据库的相关信息(包括运行ID、复制信息等)从而实现新节点的自动发现。配置哨兵监控 Redis 主从系统时只需要指定主库的信息即可,因为哨兵正是借助info命令来获取所有复制该主库的从库信息的。
    启动后,哨兵向主库发送info命令得到从库列表,而后对每个从库同样建立两个连接,两个连接的作用和与主库建立的两个连接完全一致。在此之后,哨兵会每 10 秒定时向已知的所有主从库发送info命令来获取更新信息,并进行相应操作。比如对新增的从库建立连接并加入监控列表,对主从库的角色变化(由故障恢复操作引起)进行信息更新等。
    2、每2秒向主库和从库的”sentinel:hello”频道发送自己的信息,与同样监控该数据库的哨兵分享自己的信息。也就是说哨兵不但订阅了该频道,而且还会向该频道发布信息,以使其他哨兵得到自己的信息;
    发送的消息内容为:
    <哨兵的地址>,<哨兵的端口>, <哨兵运行ID>, <哨兵的配置版本>, <主库的名字>, <主库的地址>, <主库的端口>, <主库的配置版本>
    哨兵会订阅每个其监控的数据库的”sentinel:hello”频道,所以当其他哨兵收到消息后,会判断发消息的哨兵是不是新发现的哨兵。如果是,则将其加入已发现的哨兵列表中并创建一个到其的连接(与数据库不同,哨兵与哨兵之间只会创建一条连接用来发送ping命令,而不需要创建另外一条连接来订阅频道,因为哨兵只需要订阅数据库的频道即可实现自动发现其他哨兵)。
    同时,哨兵会判断信息中主库的配置版本,如果该版本比当前记录的主库的版本高,则更新主库的数据。
    3、每1秒向主库、从库和其他哨兵节点发送ping命令。
    发送ping的时间间隔与”down-after-milliseconds”选项有关,最长间隔为1秒。当”down-after-milliseconds”的值小于1秒时,哨兵会每隔”down-after-milliseconds”指定的时间发送一次ping命令,当down-after-milliseconds的值大于1秒时,哨兵会每隔1秒发送一次ping命令。
    如果超过”down-after-milliseconds”指定时间后,被ping的节点仍未回复,则哨兵认为其主观下线(subjectively down)。主观下线表示,从当前的哨兵进程看来,该节点已经下线。如果该节点是主库,则哨兵会进一步判断是否需要对其进行故障恢复:哨兵发送”SENTINEL is-master-down-by-addr”命令询问其他哨兵节点以了解他们是否也认为该主库主观下线,如 果达到指定数量时,哨兵会认为其客观下线(objectively down),并选举领头的哨兵节点发起故障恢复。例如sentinel monitor master15 10.15.43.15 6979 3表示只有当quorum至少3个哨兵节点(包括当前节点)认为该主库主观下线时,当前哨兵节点才会认为该主库客观下线。
    当哨兵节点发现了主库客观下线,需要故障恢复,故障恢复需要由领头的哨兵来完成,这样可以保证同一时间只有一个哨兵节点来执行故障恢复。选举领头哨兵的过程使用了 Raft算法:
    1、发现主库客观下线的哨兵节点(哨兵A)向每个哨兵节点发送命令,要求对方选自己成为领头哨兵。
    2、如果目标哨兵节点没有选过其他人,则会同意将哨兵A设置成领头哨兵。
    3、如果哨兵A发现有超过半数且超过quorum参数值的哨兵节点同意选自己成为领头哨兵,则哨兵A成功成为领头哨兵。
    4、若有多个哨兵节点同时参选领头哨兵,则会出现没有任何节点当选的可能。此时每个参选节点将等待一个随机时间重新发起参选请求,进行下一轮选举,直到选举成功。
    选出领头哨兵后,领头哨兵开始对主库进行故障恢复:
    a、领头哨兵将从停止服务的主库的从库中挑选一个来充当新的主库:
    1、所有在线的从库中,选择优先级最高的从库。优先级可以通过”slave-priority”选项来设置;
    2、如果有多个最高优先级的从库,则复制的命令偏移量越大(即复制越完整)越优先;
    3、如果以上条件都一样,则选择运行ID较小的从库。
    b、领头哨兵将向从库发送”slaveof no one”命令,使其升级为主库。而后领头哨兵向其他从库发送slaveof命令来使其成为新主库的从库。
    c、更新内部记录,将已经停止服务的,旧的主库更新为新的主库的从库,使得当其恢复服务时自动以从库的身份继续服务。
    如果一个主从系统中配置的哨兵较少,哨兵对整个系统的判断的可靠性就会降低。当节点较少时建议为每个节点(无论是主库还是从库)部署一个哨兵,同时设置 quorum 的值为 N/2 + 1(其中N为哨兵节点数量);当系统中的节点较多时,考虑到每个哨兵都会和系统中的所有节点建立连接,为每个节点分配一个哨兵会产生较多连接,尤其是当进行客户端分片时使用多个哨兵节点监控多个主库,会因为 Redis 不支持连接复用而产生大量冗余连接
    slave的选举主要会评估slave的以下几个方面:
    与master断开连接的次数
    Slave的优先级
    数据复制的下标(用来评估slave当前拥有多少master的数据)
    进程ID
    如果一个slave与master失去联系超过10次,并且每次都超过了配置的最大失联时间(down-after-milliseconds),如果sentinel在进行failover时发现slave失联,那么这个slave就会被sentinel认为不适合用来做新master的。

  • 为什么要先获得大多数sentinel的认可时才能真正去执行failover呢?

当一个sentinel被授权后,它将会获得宕掉的master的一份最新配置版本号,当failover执行结束以后,这个版本号将会被用于最新的配置。因为大多数sentinel都已经知道该版本号已经被要执行failover的sentinel拿走了,所以其他的sentinel都不能再去使用这个版本号。这意味着,每次failover都会附带有一个独一无二的版本号。我们将会看到这样做的重要性。

而且,sentinel集群都遵守一个规则:如果sentinel A推荐sentinel B去执行failover,B会等待一段时间后,自行再次去对同一个master执行failover,这个等待的时间是通过failover-timeout配置项去配置的。从这个规则可以看出,sentinel集群中的sentinel不会再同一时刻并发去failover同一个master,第一个进行failover的sentinel如果失败了,另外一个将会在一定时间内进行重新进行failover,以此类推。

redis sentinel保证了活跃性:如果大多数sentinel能够互相通信,最终将会有一个被授权去进行failover.
redis sentinel也保证了安全性:每个试图去failover同一个master的sentinel都会得到一个独一无二的版本号。

sentinel集群中各个sentinel也有互相通信,通过gossip协议

一旦一个sentinel成功地对一个master进行了failover,它将会把关于master的最新配置通过广播形式通知其它sentinel,其它的sentinel则更新对应master的配置。

一个faiover要想被成功实行,sentinel必须能够向选为master的slave发送SLAVEOF NO ONE命令,然后能够通过INFO命令看到新master的配置信息。

当将一个slave选举为master并发送SLAVEOF NO ONE后,即使其它的slave还没针对新master重新配置自己,failover也被认为是成功了的,然后所有sentinels将会发布新的配置信息。

新配在集群中相互传播的方式,就是为什么我们需要当一个sentinel进行failover时必须被授权一个版本号的原因。

即使当前没有failover正在进行,sentinel依然会使用当前配置去设置监控的master,当最新配置确认为slaves的节点却声称自己是master,这时它们会被重新配置为当前master的slave。如果slaves连接了一个错误的master,将会被改正过来,连接到正确的master。

每个sentinel使用##发布/订阅##的方式持续地传播master的配置版本信息,配置传播的##发布/订阅##管道是:sentinel:hello。

因为每一个配置都有一个版本号,所以以版本号最大的那个为标准。


不同机房部署redis主从

有三个主机,每个主机分别运行一个redis和一个sentinel,redis2和redis3在一个机房A,redis1在另外一个机房B,当机房A与机房B网络中断时,sentinel3和sentinel2启动了failover并把redis2选举为master。此时sentinel1依然是旧的配置,因为它与sentinel3、sentinel2隔离了。当网络恢复以后,sentinel1将会更新它的配置,讲redis1变成redis2的slave,而在网络断开期间客户端依然可以向redis1写入数据,这样网络恢复后,客户端在网络断开期间写入redis1的数据就会丢失。
Redis主从复制(读写分离)、哨兵(主从切换)配置

Redis的min-slaves-to-write和min-slaves-max-lag两个选项可以防止主服务器在不安全的情况下执行写命令。
这个时候可以通过修改redis配置,让网络断开期间redis1拒绝客户端的写请求。min-slaves-to-write和min-slaves-max-lag两个选项可以防止主服务器在不安全的情况下执行写命令。
min-slaves-to-write 2
min-slaves-max-lag 10
从服务器的数量少于2个,或者2个从服务器的延迟(lag)值都大于或等于10秒时,主服务器将拒绝执行写命令,这里的延迟值是INFO replication命令的lag值。