1、Redis复制功能简单介绍

  • 使用异步复制
    - 一个主服务器可以有自己的多个从服务器
    - 从服务器也可以有自己的从服务器
    - 复制功能不会阻塞主服务器
    - 可以通过复制功能来让主服务器免于执行持久化操作,由从服务器去执行持久化操作即可。

2、Redis复制功能介绍

  • Redis使用异步复制。从Redis2.8开始,从服务器会以每秒一次的频率向主服务器报告复制流(replication stream)的处理进度。
    - 一个主服务器可以有多个从服务器。
    - 不仅主服务器可以有从服务器,从服务器也可以有自己的从服务器,多个从服务器之间可以构成一个图状结构。
    - 复制功能不会阻塞主服务器:即使有一个或多个从服务器正在进行初次同步,主服务器也可以继续处理命令请求。
    - 复制功能也不会阻塞从服务器:只要再redis.conf文件中进行了相应的设置,即使从服务器正在进行初次同步,服务器也可以使用旧版本的数据集来处理命令查阅。
    - 在从服务器删除旧版本数据集并载入新版本数据集的那段时间内,连接请求会被阻塞。
    - 还可以配置从服务器,让它在与主服务器之间的连接断开时,向客户端发送一个错误。
    - 复制功能可以单纯地用于数据冗余(data redundancy),也可以通过让多个服务器处理只读命令请求来提升扩展性(scalability):比如说,繁重地SORT命令可以交给附属节点去运行。
    - 可以通过复制功能来让主服务器免于执行持久化操作:只要关闭主服务器的持久化功能,然后由从服务器去执行持久化操作即可。

3、Redis架构图

Redis主从复制 - 图1

4、关闭主服务器持久化时,复制功能的数据安全

1.当配置Redis复制功能时,强烈建议打开主服务器的持久化功能。 否则的话,由于延迟等问题,部署的服务应该要避免自动拉起。
2.为了帮助理解主服务器关闭持久化时自动拉起的危险性,参考一下以下会导致主从服务器数据全部丢失的例子:
1)假设节点A为主服务器,并且关闭了持久化。并且节点B和节点C从节点A复制数据
2)节点A崩溃,然后由自动拉起服务重启了节点A. 由于节点A的持久化被关闭了,所以重启之后没有任何数据
3)节点B和节点C将从节点A复制数据,但是A的数据是空的,于是就把自身保存的数据副本删除。
结论:
1)在关闭主服务器上的持久化,并同时开启自动拉起进程的情况下,即便使用Sentinel来实现Redis的高可用性,也是非常危险的。因为主服务器可能拉起得非常快,以至于Sentinel在配置的心跳时 间间隔内没有检测到主服务器已被重启,然后还是会执行上面的数据丢失的流程。
2)无论何时,数据安全都是极其重要的,所以应该禁止主服务器关闭持久化的同时自动拉起

5、主从复制原理

Redis主从复制 - 图2

  1. 从服务器向主服务器发送sync命令。
  2. 接收到sync命令的主服务器会调用bgsave命令,创建一个RDB文件,并使用缓冲区记录接下来执行的所有写文件。
  3. 当主服务器执行完bgsave命令时,它回向从服务器发送rdb文件,而从服务器会接收并载入这个文件。
  4. 主服务器将缓冲区存储的所有命令发送给从服务器执行。

开启主从复制

  1. ## 在从库上执行,选择主库的Ip和port
  2. 127.0.0.1:6380> slaveof 127.0.0.1 6379
  3. OK
  4. 127.0.0.1:6380> info replication
  5. # Replication
  6. role:slave ## 此角色是从库
  7. master_host:127.0.0.1 ## 主库的IP
  8. master_port:6379 ## 主库的port
  9. master_link_status:down
  10. master_last_io_seconds_ago:-1
  11. master_sync_in_progress:0
  12. slave_repl_offset:1
  13. master_link_down_since_seconds:1587636952
  14. slave_priority:100
  15. slave_read_only:1
  16. connected_slaves:0
  17. master_replid:4d901644e61202b7f3284f9b8c3bce4863394d7d
  18. master_replid2:0000000000000000000000000000000000000000
  19. master_repl_offset:0
  20. second_repl_offset:-1
  21. repl_backlog_active:0
  22. repl_backlog_size:1048576
  23. repl_backlog_first_byte_offset:0
  24. repl_backlog_histlen:0

命令传播:
在主服务器完成同步之后,主服务每执行一个写命令,它都会将执行的写命令发送给从服务器执行,这个操作被称为”命令传播”(command propagate)。
命令传播是一个持续的过程:只要复制仍在继续,命令传播就会一直进行,使得主从服务器状态可以一直保持一致。

6、Redis主从实践

环境:主库(master),ip(172.17.0.9),端口(6379)。
从库(slave01),ip(172.17.0.9),端口(6380)。
从库(slave02),ip(172.17.0.9),端口(6381)。

安装服务端见Redis安装

部署的目标环境为

[root@docker02 ~]# mkdir /data/redis/{6379,6380,6381} -p

## 从库的配置文件需要添加主库的密码,自身密码不受影响
## master:6379配置文件
[root@docker02 6379]# cd /data/redis/6379/
[root@docker02 6379]# ll
total 8
drwxr-xr-x 2 root root 4096 Apr 27 15:36 logs
-rw-r--r-- 1 root root  205 Apr 20 11:07 redis.conf
[root@docker02 6379]# cat redis.conf 
daemonize yes
pidfile /data/redis/6379/redis-6379.pid
port 6379
bind 127.0.0.1
timeout 300
loglevel notice
logfile /data/redis/6379/logs/redis-6379.log
syslog-enabled no
databases 16
requirepass redis.123

## slave01:6380配置文件
[root@docker02 6379]# cd /data/redis/6380/
[root@docker02 6380]# ll
total 8
drwxr-xr-x 2 root root 4096 Apr 20 11:15 logs
-rw-r--r-- 1 root root  206 Apr 20 11:17 redis.conf
[root@docker02 6380]# cat redis.conf 
daemonize yes
pidfile /data/redis/6380/redis-6380.pid
port 6380
bind 127.0.0.1
timeout 300
loglevel notice
logfile /data/redis/6380/logs/redis-6380.log
syslog-enabled no
databases 16
requirepass redis.123
masterauth redis.123

## salve02:6381配置文件
[root@docker02 6380]# cd /data/redis/6381
[root@docker02 6381]# ll
total 8
drwxr-xr-x 2 root root 4096 Apr 20 11:19 logs
-rw-r--r-- 1 root root  205 Apr 20 11:19 redis.conf
[root@docker02 6381]# cat redis.conf 
daemonize yes
pidfile /data/redis/6381/redis-6381.pid
port 6381
bind 127.0.0.1
timeout 300
loglevel notice
logfile /data/redis/6381/logs/redis-6381.log
syslog-enabled no
databases 16
requirepass redis.123
masterauth redis.123

## 启动master,slave01,slave02
[root@docker02 ~]# redis-server /data/redis/6379/redis.conf 
[root@docker02 ~]# redis-server /data/redis/6380/redis.conf 
[root@docker02 ~]# redis-server /data/redis/6381/redis.conf 
[root@docker02 ~]# ps -ef|grep 'redis'
root     15924     1  0 15:41 ?        00:00:00 redis-server 127.0.0.1:6379
root     15949     1  0 15:41 ?        00:00:00 redis-server 127.0.0.1:6380
root     15978     1  0 15:41 ?        00:00:00 redis-server 127.0.0.1:6381

开启Redis的主从

## 登陆slave01设置主从的主库地址连接
[root@docker02 ~]# redis-cli -p 6380
127.0.0.1:6380> auth redis.123
OK
127.0.0.1:6380> slaveof 127.0.0.1 6379
OK
127.0.0.1:6380> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:up    # up说明开启成功
master_last_io_seconds_ago:6
master_sync_in_progress:0
slave_repl_offset:0
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:d3f60c4acec3732bea31bb67e339d7250b7ce417
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:0
127.0.0.1:6380> 

## 同理,设置slave02
[root@docker02 ~]# redis-cli -p 6381
127.0.0.1:6381> auth redis.123
OK
127.0.0.1:6381> slaveof 127.0.0.1 6379
OK
127.0.0.1:6381> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:up
master_last_io_seconds_ago:2
master_sync_in_progress:0
slave_repl_offset:154
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:d3f60c4acec3732bea31bb67e339d7250b7ce417
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:154
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:155
repl_backlog_histlen:0

## 连接master:6379,查看主从情况
[root@docker02 ~]# redis-cli -p 6379
127.0.0.1:6379> auth redis.123
OK
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6380,state=online,offset=224,lag=1
slave1:ip=127.0.0.1,port=6381,state=online,offset=224,lag=1
master_replid:d3f60c4acec3732bea31bb67e339d7250b7ce417
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:224
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:224

主从切换

模拟主库宕机

## 关闭master:6379进程
[root@docker02 ~]# ps -ef|grep '6379'
root     18514     1  0 15:49 ?        00:00:00 redis-server 127.0.0.1:6379
root     21612  8966  0 15:59 pts/0    00:00:00 grep --color=auto 6379
[root@docker02 ~]# kill 18514   ##杀死master:6379进程,慎用

## 登陆slabe01
[root@docker02 ~]# redis-cli -p 6380
127.0.0.1:6380> auth redis.123
OK
127.0.0.1:6380> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:down        ## 此时未连接主库
master_last_io_seconds_ago:-1
master_sync_in_progress:0
slave_repl_offset:602
master_link_down_since_seconds:205
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:d3f60c4acec3732bea31bb67e339d7250b7ce417
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:602
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:602

## 取消slave01和master的主从关系
127.0.0.1:6380> slaveof no one
OK
127.0.0.1:6380> info replication
# Replication
role:master    ## 此时自身变为master
connected_slaves:0
master_replid:cd9eb3e74431ab0bd1b087ebd755ee85e1fda5aa
master_replid2:d3f60c4acec3732bea31bb67e339d7250b7ce417
master_repl_offset:602
second_repl_offset:603
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:602

## 将其他从库指向slave01(新的主库)
[root@docker02 ~]# redis-cli -p 6381
127.0.0.1:6381> auth redis.123
OK
127.0.0.1:6381> slaveof 127.0.0.1 6380
OK
127.0.0.1:6381> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6380
master_link_status:up
master_last_io_seconds_ago:1
master_sync_in_progress:0
slave_repl_offset:602
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:cd9eb3e74431ab0bd1b087ebd755ee85e1fda5aa
master_replid2:d3f60c4acec3732bea31bb67e339d7250b7ce417
master_repl_offset:602
second_repl_offset:603
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:155
repl_backlog_histlen:448

【注意】一般情况下,用来做主从的redis密码需要一致,如上案例中,原master:6379的配置文件中未指定主库的连接密码,导致此master不能作为slave连接其他master,原因是密码验证不通过。如果需要进行master自愈后将slave01作为主库,则需要在配置文件中添加 masterauth 。