全网最清晰!Redis主从复制原理及实现,绝对给你非一般的体验
前言
相信很多小伙伴都已经配置过主从复制,但是对于redis主从复制的工作流程和原理等问题很多都没有深入的了解。今天笔者就为大家来梳理一下Redis主从复制的原理及实践,希望能帮到大家更好的提升哦~~~
Redis主从复制原理
和Mysql主从复制的原因一样,Redis虽然读取写入的速度都特别快,但是也会产生读压力特别大的情况。为了分担读压力,Redis支持主从复制,Redis的主从结构可以采用一主多从或者级联结构,Redis主从复制可以根据是否是全量分为全量同步和增量同步。下图为级联结构。
全量同步
Redis全量复制一般发生在Slave初始化阶段,这时Slave需要将Master上的所有数据都复制一份。具体步骤如下:
- 从服务器连接主服务器,发送SYNC命令;
- 主服务器接收到SYNC命名后,开始执行BGSAVE命令生成RDB文件并使用缓冲区记录此后执行的所有写命令;
- 主服务器BGSAVE执行完后,向所有从服务器发送快照文件,并在发送期间继续记录被执行的写命令;
- 从服务器收到快照文件后丢弃所有旧数据,载入收到的快照;
- 主服务器快照发送完毕后开始向从服务器发送缓冲区中的写命令;
- 从服务器完成对快照的载入,开始接收命令请求,并执行来自主服务器缓冲区的写命令;
完成上面几个步骤后就完成了从服务器数据初始化的所有操作,从服务器此时可以接收来自用户的读请求。
增量同步
Redis增量复制是指Slave初始化后开始正常工作时主服务器发生的写操作同步到从服务器的过程。 增量复制的过程主要是主服务器每执行一个写命令就会向从服务器发送相同的写命令,从服务器接收并执行收到的写命令。
Redis主从同步策略
主从刚刚连接的时候,进行全量同步;全同步结束后,进行增量同步。当然,如果有需要,slave 在任何时候都可以发起全量同步。redis 策略是,无论如何,首先会尝试进行增量同步,如不成功,要求从机进行全量同步。
注意点
如果多个Slave断线了,需要重启的时候,因为只要Slave启动,就会发送sync请求和主机全量同步,当多个同时出现的时候,可能会导致Master IO剧增宕机。
Redis主从复制的配置十分简单,它可以使从服务器是主服务器的完全拷贝。需要清除Redis主从复制的几点重要内容:
- Redis使用异步复制。但从Redis 2.8开始,从服务器会周期性的应答从复制流中处理的数据量。
- 一个主服务器可以有多个从服务器。
- 从服务器也可以接受其他从服务器的连接。除了多个从服务器连接到一个主服务器之外,多个从服务器也可以连接到一个从服务器上,形成一个图状结构。
- Redis主从复制不阻塞主服务器端。也就是说当若干个从服务器在进行初始同步时,主服务器仍然可以处理请求。
- 主从复制也不阻塞从服务器端。当从服务器进行初始同步时,它使用旧版本的数据来应对查询请求,假设你在redis.conf配置文件是这么配置的。否则的话,你可以配置当复制流关闭时让从服务器给客户端返回一个错误。但是,当初始同步完成后,需要删除旧的数据集和加载新的数据集,在这个短暂的时间内,从服务器会阻塞连接进来的请求。
- 主从复制可以用来增强扩展性,使用多个从服务器来处理只读的请求(比如,繁重的排序操作可以放到从服务器去做),也可以简单的用来做数据冗余。
- 使用主从复制可以为主服务器免除把数据写入磁盘的消耗:在主服务器的redis.conf文件中配置“避免保存”(注释掉所有“保存“命令),然后连接一个配置为“进行保存”的从服务器即可。但是这个配置要确保主服务器不会自动重启(要获得更多信息请阅读下一段)
主从复制的一些特点:
- 采用异步复制;
- 一个主redis可以含有多个从redis;
- 每个从redis可以接收来自其他从redis服务器的连接;
- 主从复制对于主redis服务器来说是非阻塞的,这意味着当从服务器在进行主从复制同步过程中,主redis仍然可以处理外界的访问请求;
- 主从复制对于从redis服务器来说也是非阻塞的,这意味着,即使从redis在进行主从复制过程中也可以接受外界的查询请求,只不过这时候从redis返回的是以前老的数据,如果你不想这样,那么在启动redis时,可以在配置文件中进行设置,那么从redis在复制同步过程中来自外界的查询请求都会返回错误给客户端;(虽然说主从复制过程中对于从redis是非阻塞的,但是当从redis从主redis同步过来最新的数据后还需要将新数据加载到内存中,在加载到内存的过程中是阻塞的,在这段时间内的请求将会被阻,但是即使对于大数据集,加载到内存的时间也是比较多的);
- 主从复制提高了redis服务的扩展性,避免单个redis服务器的读写访问压力过大的问题,同时也可以给为数据备份及冗余提供一种解决方案;
- 为了编码主redis服务器写磁盘压力带来的开销,可以配置让主redis不再将数据持久化到磁盘,而是通过连接让一个配置的从redis服务器及时的将相关数据持久化到磁盘,不过这样会存在一个问题,就是主redis服务器一旦重启,因为主redis服务器数据为空,这时候通过主从同步可能导致从redis服务器上的数据也被清空;
主从复制的作用
主从复制,读写分离,容灾恢复。一台主机负责写入数据,多台从机负责备份数据。在高并发的场景下,即便是主机挂了,可以用从机代替主机继续工作,避免单点故障导致系统性能问题。读写分离,让读多写少的应用性能更佳。
主从复制架构
At the base of Redis replication there is a very simple to use and configure master-slave replication that allows slave Redis servers to be exact copies of master servers.
官方说了,搭建主从架构is a very simple 。确实是简单的,一个命令: slaveof 主机ip 主机port ,就可以确定主从关系;一个命令:./redis-sentinel sentinel.conf ,就可以开启哨兵监控。搭建是简单的,维护是痛苦的。在高并发场景下,会有很多想不到的问题出现。我们只有清楚复制的原理,熟悉主机,从机宕机后的变化。才能很好的跨过这些坑。
架构图:一主二仆一兵(也可以多主多仆多兵)
搭建前的准备工作
因为穷,笔者选择用一台服务器模拟三台主机。和生产环境的区别仅仅是ip地址和port端口不同。
- 第一步:将redis.conf 拷贝三份,名字分别是,redis6379.conf,redis6380.conf,redis6381.conf
- 第二步:修改三个文件的port端口,pid文件名,日志文件名,rdb文件名
- 第三步:分别打开三个窗口模拟三台服务器,开启redis服务。
[aaa@qq.com bin]# cp redis.conf redis6379.conf
[aaa@qq.com bin]# cp redis.conf redis6380.conf
[aaa@qq.com bin]# cp redis.conf redis6381.conf
[aaa@qq.com bin]# vim redis6379.conf
logfile "6379.log"
dbfilename dump_6379.rdb
[aaa@qq.com bin]# vim redis6380.conf
pidfile /var/run/redis_6380.pid
port 6380
logfile "6380.log"
dbfilename dump_6380.rdb
[aaa@qq.com bin]# vim redis6381.conf
port 6381
pidfile /var/run/redis_6381.pid
logfile "6381.log"
dbfilename dump_6381.rdb
[aaa@qq.com bin]# ./redis-server redis6379.conf
[aaa@qq.com bin]# ./redis-cli -h 127.0.0.1 -p 6379
127.0.0.1:6379> keys *
(empty list or set)
[aaa@qq.com bin]# ./redis-server redis6380.conf
[aaa@qq.com bin]# ./redis-cli -h 127.0.0.1 -p 6380
127.0.0.1:6380> keys *
(empty list or set)
[aaa@qq.com bin]# ./redis-server redis6381.conf
[aaa@qq.com bin]# ./redis-cli -h 127.0.0.1 -p 6381
127.0.0.1:6381> keys *
(empty list or set)
接下来咱们再来介绍主从复制搭建步骤
基础搭建
第一步:查询主从复制信息,分别选择三个端口,执行命令:info replication。
# 6379 端口
[aaa@qq.com bin]# ./redis-server redis6379.conf
[aaa@qq.com bin]# ./redis-cli -h 127.0.0.1 -p 6379
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:0
......
# 6380 端口
127.0.0.1:6380> info replication
# Replication
role:master
connected_slaves:0
......
# 6381 端口
127.0.0.1:6381> info replication
# Replication
role:master
connected_slaves:0
......
三个端口都打印相同的信息:role:master 角色是master,connected_slaves:0 连接从机数量为零。
第二步:选择6379端口,执行命令:set k1 v1
127.0.0.1:6379> set k1 v1
OK
第三步:设置主从关系,分别选择6380端口和6381端口,执行命令:SLAVEOF 127.0.0.1 6379
# 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
master_port:6379
......
# 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
master_port:6379
......
# 6379 端口
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6380,state=online,offset=98,lag=1
slave1:ip=127.0.0.1,port=6381,state=online,offset=98,lag=1
......
主从关系发生了变化:6380端口和6381端口打印的信息: role:slave 从机;master_host:127.0.0.1 主机的ip地址;master_port:6379 主机的port 端口。6379端口打印的信息: role:master 主机;connected_slaves:2 连了两个从机; slaveX : ID、IP 地址、端口号、连接状态、从库信息
第四步:全量复制,分别选择6380端口和6381端口,执行命令:get k1
# 6380 端口
127.0.0.1:6380> get k1
"v1"
# 6381 端口
127.0.0.1:6381> get k1
"v1"
两个端口都可以打印k1的值,说明在建立主从关系时,从机便拥有了主机的数据。
第五步:增量复制,选择6379端口,执行命令:set k2 v2。然后分别选择6380端口和6381端口,执行命令:get k2
# 6379 端口
127.0.0.1:6379> set k2 v2
OK
# 6380 端口
127.0.0.1:6380> get k2
"v2"
# 6381 端口
127.0.0.1:6381> get k2
"v2"
两个端口都可以打印k2的值,说明建立主从关系后,主机新增的数据都会复制给从机。
第六步:主从的读写分离,选择6380端口,执行命令:set k3 v3
# 6380 端口
127.0.0.1:6380> set k3 v3
(error) READONLY You can't write against a read only slave.
# 6379 端口
127.0.0.1:6379> set k3 v3
OK
从机6380写入失败,是因为读写分离的机制。
第七步:主机宕机的情况,选择6379端口,执行命令:shutdown
# 6379 端口
127.0.0.1:6379> SHUTDOWN
not connected> QUIT
# 6380 端口
127.0.0.1:6380> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
......
# 6381 端口
127.0.0.1:6381> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
......
从打印的结果得知:从机原地待命
第八步:主机宕机后恢复,选择6379端口,重启Redis服务,执行命令:set k4 v4。分别选择6380端口和6381端口,执行命令:get k4
# 6379 端口
[aaa@qq.com bin]# ./redis-server redis6379.conf
[aaa@qq.com bin]# ./redis-cli -h 127.0.0.1 -p 6379
127.0.0.1:6379> set k4 v4
OK
# 6380 端口
127.0.0.1:6380> get k4
"v4"
# 6381 端口
127.0.0.1:6381> get k4
"v4"
主机重启后,一切正常。
第九步:从机宕机后恢复,选择6380端口,执行命令:shutdown。选择6379端口,执行命令:set k5 v5。选择6380端口,重启Redis服务后执行命令:get k5
# 6380 端口
127.0.0.1:6380> SHUTDOWN
not connected> QUIT
[aaa@qq.com bin]# ./redis-server redis6380.conf
[aaa@qq.com bin]# ./redis-cli -h 127.0.0.1 -p 6380
127.0.0.1:6380> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
......
127.0.0.1:6380> get k5
"v5"
# 6379 端口
127.0.0.1:6379> set k5 v5
OK
从机宕机后,一切正常。笔者用的是redis.4.0.2版本的。看过其他教程,从机宕机恢复后,只能同步主机新增数据,也就是k5是没有值的,可是笔者反复试过,均有值。留着备忘!
第十步:去中性化思想,选择6380端口,执行命令:SLAVEOF 127.0.0.1 6381。选择6381端口,执行命令:info replication
# 6380 端口
127.0.0.1:6380> SLAVEOF 127.0.0.1 6381
OK
# 6381 端口
127.0.0.1:6381> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
......
connected_slaves:1
slave0:ip=127.0.0.1,port=6380,state=online,offset=1677,lag=1
......
虽然6381 是6380的主机,是6379的从机。在Redis眼中,6381依旧是从机。一台主机配多台从机,一台从机再配多台从机,从而实现了庞大的集群架构。同时也减轻了一台主机的压力,缺点是增加了服务器间的延迟。
从机上位
模拟主机宕机,人为手动怂恿从机上位的场景。先将三个端口恢复成6379是主机,6380和6381是从机的架构。从机上位步骤:第一步:模拟主机宕机,选择6379端口,执行命令:shutdown第二步:断开主从关系,选择6380端口,执行命令:SLAVEOF no one第三步:重新搭建主从,选择6381端口,执行命令:info replication,SLAVEOF 127.0.0.1 6380第四步:之前主机恢复,选择6379端口,重启Redis服务,执行命令:info replication在6379主机宕机后,6380从机断开主从关系,6381开始还在原地待命,后来投靠6380主机后,6379主机回来了当它已是孤寡老人,空头司令。
# 6379端口
127.0.0.1:6379> SHUTDOWN
not connected> QUIT
# 6380端口
127.0.0.1:6380> SLAVEOF no one
OK
127.0.0.1:6380> set k6 v6
OK
# 6381端口
127.0.0.1:6381> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
......
127.0.0.1:6381> SLAVEOF 127.0.0.1 6380
OK
127.0.0.1:6381> get k6
"v6"
哨兵监控
从机上位是需要人为控制,在生产环境中是不可取的,不可能有人实时盯着它,也不可能大半夜起床重新搭建主从关系。在这样的需求促使下,哨兵模式来了!!!哨兵有三大任务:
- 监控:哨兵会不断地检查你的Master和Slave是否运作正常
- 提醒:当被监控的某个Redis出现问题时, 哨兵可以通过API向管理员或者其他应用程序发送通知
- 故障迁移:若一台主机出现问题时,哨兵会自动将该主机下的某一个从机设置为新的主机,并让其他从机和新主机建立主从关系。
哨兵搭建步骤:
第一步:新开一个窗口,取名sentinel,方便观察哨兵日志信息。
第二步:创建sentinel.conf文件,也可以从redis的解压文件夹中拷贝一份。
第三步:设置监控的主机和上位的规则,编辑sentinel.conf,输入 sentinel monitor itdragon-redis 127.0.0.1 6379 1 保存退出。解说:指定监控主机的ip地址,port端口,得票数。
第四步:前端启动哨兵,执行命令:./redis-sentinel sentinel.conf。
第五步:模拟主机宕机,选择6379窗口,执行命令:shutdown。
第六步:等待从机投票,在sentinel窗口中查看打印信息。
第七步:启动6379服务器。
语法结构:sentinel monitor 自定义数据库名主机ip 主机port 得票数若从机得票数大于设置值,则成为新的主机。若之前的主机恢复后,如果哨兵也宕机了???那就多配几个哨兵并且相互监控。
# sentinel窗口
[aaa@qq.com bin]# vim sentinel.conf
sentinel monitor itdragon-redis 127.0.0.1 6379 1
[aaa@qq.com bin]# ./redis-sentinel sentinel.conf
......
21401:X 29 Nov 15:39:15.052 * +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ itdragon-redis 127.0.0.1 6380
21401:X 29 Nov 15:39:15.052 * +slave slave 127.0.0.1:6379 127.0.0.1 6379 @ itdragon-redis 127.0.0.1 6380
21401:X 29 Nov 15:39:45.081 # +sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ itdragon-redis 127.0.0.1 6380
21401:X 29 Nov 16:40:52.055 # -sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ itdragon-redis 127.0.0.1 6380
21401:X 29 Nov 16:41:02.028 * +convert-to-slave slave 127.0.0.1:6379 127.0.0.1 6379 @ itdragon-redis 127.0.0.1 6380
......
# 6379端口
127.0.0.1:6379> SHUTDOWN
not connected> QUIT
# 6380端口
127.0.0.1:6380> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=127.0.0.1,port=6381,state=online,offset=72590,lag=0
......
# 6381端口
127.0.0.1:6381> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6380
......
- +slave :一个新的从服务器已经被 Sentinel 识别并关联。
- +sdown :给定的实例现在处于主观下线状态。
- -sdown :给定的实例已经不再处于主观下线状态。
主从复制的缺点
Redis的主从复制最大的缺点就是延迟,主机负责写,从机负责备份,这个过程有一定的延迟,当系统很繁忙的时候,延迟问题会更加严重,从机器数量的增加也会使这个问题更加严重。
总结
- 查看主从复制关系命令:info replication
- 设置主从关系命令:slaveof 主机ip 主机port
- 开启哨兵模式命令:./redis-sentinel sentinel.conf
- 主从复制原则:开始是全量赋值,之后是增量赋值
- 哨兵模式三大任务:监控,提醒,自动故障迁移
好了,以上就是笔者整理的Redis主从复制原理及实现,只是个人的一些想法,有哪里不准确的地方,还请各位大佬评论区交流,咱们共同交流进步~~~
喜欢文章请多多点赞评论转发,关注小编,你们的支持就是小编创作的不竭动力~~~