1、Redis复制功能简单介绍
- 使用异步复制
- 一个主服务器可以有自己的多个从服务器
- 从服务器也可以有自己的从服务器
- 复制功能不会阻塞主服务器
- 可以通过复制功能来让主服务器免于执行持久化操作,由从服务器去执行持久化操作即可。
2、Redis复制功能介绍
- Redis使用异步复制。从Redis2.8开始,从服务器会以每秒一次的频率向主服务器报告复制流(replication stream)的处理进度。
- 一个主服务器可以有多个从服务器。
- 不仅主服务器可以有从服务器,从服务器也可以有自己的从服务器,多个从服务器之间可以构成一个图状结构。
- 复制功能不会阻塞主服务器:即使有一个或多个从服务器正在进行初次同步,主服务器也可以继续处理命令请求。
- 复制功能也不会阻塞从服务器:只要再redis.conf文件中进行了相应的设置,即使从服务器正在进行初次同步,服务器也可以使用旧版本的数据集来处理命令查阅。
- 在从服务器删除旧版本数据集并载入新版本数据集的那段时间内,连接请求会被阻塞。
- 还可以配置从服务器,让它在与主服务器之间的连接断开时,向客户端发送一个错误。
- 复制功能可以单纯地用于数据冗余(data redundancy),也可以通过让多个服务器处理只读命令请求来提升扩展性(scalability):比如说,繁重地SORT命令可以交给附属节点去运行。
- 可以通过复制功能来让主服务器免于执行持久化操作:只要关闭主服务器的持久化功能,然后由从服务器去执行持久化操作即可。
3、Redis架构图
4、关闭主服务器持久化时,复制功能的数据安全
1.当配置Redis复制功能时,强烈建议打开主服务器的持久化功能。 否则的话,由于延迟等问题,部署的服务应该要避免自动拉起。
2.为了帮助理解主服务器关闭持久化时自动拉起的危险性,参考一下以下会导致主从服务器数据全部丢失的例子:
1)假设节点A为主服务器,并且关闭了持久化。并且节点B和节点C从节点A复制数据
2)节点A崩溃,然后由自动拉起服务重启了节点A. 由于节点A的持久化被关闭了,所以重启之后没有任何数据
3)节点B和节点C将从节点A复制数据,但是A的数据是空的,于是就把自身保存的数据副本删除。
结论:
1)在关闭主服务器上的持久化,并同时开启自动拉起进程的情况下,即便使用Sentinel来实现Redis的高可用性,也是非常危险的。因为主服务器可能拉起得非常快,以至于Sentinel在配置的心跳时 间间隔内没有检测到主服务器已被重启,然后还是会执行上面的数据丢失的流程。
2)无论何时,数据安全都是极其重要的,所以应该禁止主服务器关闭持久化的同时自动拉起
5、主从复制原理
- 从服务器向主服务器发送sync命令。
- 接收到sync命令的主服务器会调用bgsave命令,创建一个RDB文件,并使用缓冲区记录接下来执行的所有写文件。
- 当主服务器执行完bgsave命令时,它回向从服务器发送rdb文件,而从服务器会接收并载入这个文件。
- 主服务器将缓冲区存储的所有命令发送给从服务器执行。
开启主从复制
## 在从库上执行,选择主库的Ip和port
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 ## 主库的IP
master_port:6379 ## 主库的port
master_link_status:down
master_last_io_seconds_ago:-1
master_sync_in_progress:0
slave_repl_offset:1
master_link_down_since_seconds:1587636952
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:4d901644e61202b7f3284f9b8c3bce4863394d7d
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
命令传播:
在主服务器完成同步之后,主服务每执行一个写命令,它都会将执行的写命令发送给从服务器执行,这个操作被称为”命令传播”(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 。