第五章· Redis主从复制介绍

一.Redis主从复制

1.Redis复制功能简单介绍

1)使用异步复制。
2)一个主服务器能够有多个从服务器。
3)从服务器也能够有本身的从服务器。
4)复制功能不会阻塞主服务器。
5)能够经过复制功能来让主服务器免于执行持久化操做,由从服务器去执行持久化操做便可。redis

2.Redis复制功能介绍(重点了解)

1)Redis 使用异步复制。从 Redis2.8开始,从服务器会以每秒一次的频率向主服务器报告复制流(replication stream)的处理进度。数据库

2)一个主服务器能够有多个从服务器。安全

3)不只主服务器能够有从服务器,从服务器也能够有本身的从服务器,多个从服务器之间能够构成一个图状结构。服务器

4)复制功能不会阻塞主服务器:即便有一个或多个从服务器正在进行初次同步, 主服务器也能够继续处理命令请求。网络

5)复制功能也不会阻塞从服务器:只要在 redis.conf 文件中进行了相应的设置, 即便从服务器正在进行初次同步, 服务器也可使用旧版本的数据集来处理命令查询。架构

6)在从服务器删除旧版本数据集并载入新版本数据集的那段时间内,链接请求会被阻塞。异步

7)还能够配置从服务器,让它在与主服务器之间的链接断开时,向客户端发送一个错误。scala

8)复制功能能够单纯地用于数据冗余(data redundancy),也能够经过让多个从服务器处理只读命令请求来提高扩展性(scalability): 好比说,繁重的SORT命令能够交给附属节点去运行。3d

9)能够经过复制功能来让主服务器免于执行持久化操做:只要关闭主服务器的持久化功能,而后由从服务器去执行持久化操做便可。code

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.主从复制的原理


图1·由一位灵魂画师画出的Redis主从复制原理图

1)从服务器向主服务器发送 SYNC 命令。

2)接到 SYNC 命令的主服务器会调用BGSAVE 命令,建立一个 RDB 文件,并使用缓冲区记录接下来执行的全部写命令。

3)当主服务器执行完 BGSAVE 命令时,它会向从服务器发送 RDB 文件,而从服务器则会接收并载入这个文件。

4)主服务器将缓冲区储存的全部写命令发送给从服务器执行。

6.开启主从复制

#开启主从复制(在从库上执行)
127.0.0.1:6379> SLAVEOF 10.0.0.51 6379
OK
#查看主从信息
127.0.0.1:6379> INFO replication
# Replication
role:slave                      //角色是从库
master_host:10.0.0.51           //主库IP是10.0.0.51
master_port:6379
master_link_status:down
master_last_io_seconds_ago:-1
master_sync_in_progress:0
slave_repl_offset:1
master_link_down_since_seconds:1540419761
slave_priority:100
slave_read_only:1
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0

二.Redis主从复制工做机制

1.SYNC命令执行示例

2.命令传播

在主从服务器完成同步以后,主服务器每执行一个写命令,它都会将被执行的写命令发送给从服务器执行,这个操做被称为“命令传播”(command propagate)。

命令传播是一个持续的过程:只要复制仍在继续,命令传播就会一直进行,使得主从服务器的状态能够一直保持一致。

3.SYNC与PSYNC

1)在 Redis2.8版本以前,断线以后重连的从服务器总要执行一次完整重同步(fullresynchronization)操做。

2)从 Redis2.8开始,Redis使用PSYNC命令代替SYNC命令。

3)PSYNC比起SYNC的最大改进在于PSYNC实现了部分重同步(partial resync)特性:
在主从服务器断线而且从新链接的时候,只要条件容许,PSYNC可让主服务器只向从服务器同步断线期间缺失的数据,而不用从新向从服务器同步整个数据库。

注:
PSYNC这个特性须要主服务器为被发送的复制流建立一个内存缓冲区(in-memory backlog), 而且主服务器和全部从服务器之间都记录一个复制偏移量(replication offset)和一个主服务器 ID(master run id),当出现网络链接断开时,从服务器会从新链接,而且向主服务器请求继续执行原来的复制进程:

1)若是从服务器记录的主服务器ID和当前要链接的主服务器的ID相同,而且从服务器记录的偏移量所指定的数据仍然保存在主服务器的复制流缓冲区里面,那么主服务器会向从服务器发送断线时缺失的那部分数据,而后复制工做能够继续执行。

2)不然的话,从服务器就要执行完整重同步操做。

4.SYNC处理断线重连示例

若是咱们仔细地观察整个断线并重连的过程,就会发现:
从服务器在断线以前已经拥有主服务器的绝大部分数据,要让主从服务器从新回到一致状态,从服务器真正须要的是 k1008七、k10088和k10089这三个键的数据,而不是主服务器整个数据库的数据。SYNC 命令在处理断线并重连时的作法——将主服务器的整个数据库从新同步给从服务器,是极度浪费的!

5.PSYNC处理断线重连示例

1)PSYNC只会将从服务器断线期间缺失的数据发送给从服务器。两个例子的状况是相同的,但SYNC 须要发送包含整个数据库的 RDB 文件,而PSYNC 只须要发送三个命令。

2)若是主从服务器所处的网络环境并不那么好的话(常常断线),那么请尽可能使用 Redis 2.8 或以上版本:经过使用 PSYNC 而不是 SYNC 来处理断线重链接,能够避免由于重复建立和传输 RDB文件而浪费大量的网络资源、计算资源和内存资源。

6.复制的一致性问题

1)在读写分离环境下,客户端向主服务器发送写命令 SET k10086 v10086,主服务器在执行这个写命令以后,向客户端返回回复,并将这个写命令传播给从服务器。

2)接到回复的客户端继续向从服务器发送读命令 GET k10086 ,而且由于网络状态的缘由,客户端的 GET命令比主服务器传播的 SET 命令更快到达了从服务器。

3)由于从服务器键k10086的值还未被更新,因此客户端在从服务器读取到的将是一个错误(过时)的k10086值。

Redis是怎么保证数据安全的呢?

1)主服务器只在有至少N个从服务器的状况下,才执行写操做

2)从Redis 2.8开始,为了保证数据的安全性,能够经过配置,让主服务器只在有至少N个当前已链接从服务器的状况下,才执行写命令。

3)不过,由于 Redis 使用异步复制,因此主服务器发送的写数据并不必定会被从服务器接收到,所以, 数据丢失的可能性仍然是存在的。

4)经过如下两个参数保证数据的安全:

#执行写操做所需的至少从服务器数量
min-slaves-to-write <number of slaves>
#指定网络延迟的最大值
min-slaves-max-lag <number of seconds>

这个特性的运做原理:

1)从服务器以每秒一次的频率 PING 主服务器一次, 并报告复制流的处理状况。主服务器会记录各个从服务器最后一次向它发送 PING 的时间。用户能够经过配置, 指定网络延迟的最大值 min-slaves-max-lag , 以及执行写操做所需的至少从服务器数量 min-slaves-to-write 。

2)若是至少有 min-slaves-to-write 个从服务器, 而且这些服务器的延迟值都少于 min-slaves-max-lag 秒, 那么主服务器就会执行客户端请求的写操做。你能够将这个特性看做 CAP 理论中的 C 的条件放宽版本: 尽管不能保证写操做的持久性, 但起码丢失数据的窗口会被严格限制在指定的秒数中。

3)另外一方面, 若是条件达不到 min-slaves-to-write 和 min-slaves-max-lag 所指定的条件, 那么写操做就不会被执行, 主服务器会向请求执行写操做的客户端返回一个错误。

7.Redis主从实践

环境

角色 主机 IP 端口
主库(master) db01 10.0.0.51 6379
从库(slave01) db01 10.0.0.51 6380
主库(slave02) db01 10.0.0.51 6381

配置多实例

#建立多实例目录
[root@db01 ~]# /etc/redis/{6379,6380,6381}
#编辑多实例配置文件
[root@db01 ~]# cat /etc/redis/6379/redis.conf /etc/redis/6380/redis.conf /etc/redis/6381/redis.conf

#redis 6379 配置文件
port 6379
daemonize yes
pidfile /etc/redis/6379/redis.pid
loglevel notice
logfile /etc/redis/6379/redis.log
dbfilename dump.rdb
dir /etc/redis/6379
bind 127.0.0.1 10.0.0.51
protected-mode no

#redis 6380 配置文件
port 6380
daemonize yes
pidfile /etc/redis/6380/redis.pid
loglevel notice
logfile /etc/redis/6380/redis.log
dbfilename dump.rdb
dir /etc/redis/6380
bind 127.0.0.1 10.0.0.51
protected-mode no

#redis 6381 配置文件
port 6381
daemonize yes
pidfile /etc/redis/6381/redis.pid
loglevel notice
logfile /etc/redis/6381/redis.log
dbfilename dump.rdb
dir /etc/redis/6381
bind 127.0.0.1 10.0.0.51
protected-mode no

#启动redis多实例
[root@db01 ~]# redis-server /etc/redis/6379/redis.conf
[root@db01 ~]# redis-server /etc/redis/6380/redis.conf
[root@db01 ~]# redis-server /etc/redis/6381/redis.conf

#查看进程
[root@db01 ~]# ps -ef|grep redis
root       3570      1  0 22:44 ?        00:00:00 redis-server 127.0.0.1:6379
root       3574      1  0 22:44 ?        00:00:00 redis-server 127.0.0.1:6380
root       3578      1  0 22:44 ?        00:00:00 redis-server 127.0.0.1:6381

开启主从

#链接从库slave01(6380)
[root@db01 ~]# redis-cli -p 6380
#开启主从
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            //主库的端口
master_link_status:up
master_last_io_seconds_ago:7
master_sync_in_progress:0
slave_repl_offset:15
slave_priority:100
slave_read_only:1
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0


#链接从库slave02(6381)
[root@db01 ~]# redis-cli -p 6381
#开启主从
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       //主库的ip
master_port:6379            //主库的端口
master_link_status:up
master_last_io_seconds_ago:9
master_sync_in_progress:0
slave_repl_offset:225
slave_priority:100
slave_read_only:1
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0


#链接master(6379)
[root@db01 ~]# redis-cli -p 6379
#在主库上查看主从复制信息
127.0.0.1:6379> INFO replication
# Replication
role:master                                                     //角色master
connected_slaves:2                                              //两台slave
slave0:ip=127.0.0.1,port=6380,state=online,offset=337,lag=1
slave1:ip=127.0.0.1,port=6381,state=online,offset=337,lag=1
master_repl_offset:337
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:336

主从切换

#链接master(6379)
[root@db01 ~]# redis-cli -p 6379
#关闭主库
127.0.0.1:6379> shutdown
#链接从库slave01(6380)
[root@db01 ~]# redis-cli -p 6380
#查看主从信息
127.0.0.1:6380> INFO replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:down                 //链接主库的状态是:down
master_last_io_seconds_ago:-1
master_sync_in_progress:0
slave_repl_offset:1877
master_link_down_since_seconds:58
slave_priority:100
slave_read_only:1
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
#取消6380的主从关系
127.0.0.1:6380> SLAVEOF no one
OK
127.0.0.1:6380> info replication
# Replication
role:master                 //此时6380的角色就变成了master
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0


#将其余从库从新指向新主(6380)
#链接6381从库
[root@db01 ~]# redis-cli -p 6381
#将6381从库变成6380的从库
127.0.0.1:6381> SLAVEOF 127.0.0.1 6380
OK
#查看主从信息
127.0.0.1:6381> INFO replication
# Replication
role:slave                      //角色仍是slave
master_host:127.0.0.1
master_port:6380                //主库的端口已经变成了6380
master_link_status:up
master_last_io_seconds_ago:4
master_sync_in_progress:0
slave_repl_offset:1
slave_priority:100
slave_read_only:1
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
相关文章
相关标签/搜索