震惊!各大互联网公司中的Redis服务器竟...
来源:左耳君(ID:qaqzuoer)
作者: 湿兄
在Redis中,我们可以将实现高可用技术划分为:持久化、主从复制、哨兵模式和集群(其实也可以不把持久化划分为高可用技术,这都无所谓)
在我们的生产环境中,如果只有一台Redis服务器来运行保存数据的话,那灾难可就大了,为啥这么说呢?如果一旦这台服务器宕机了,那是不是意味着线上环境崩了呢?暂时崩了机器没坏还好,我们可以通过前两篇说的持久化来恢复数据,但是机器要是gg了呢
那我们通过什么来恢复数据呢?如果恢复不了,那完蛋了,你可以收拾下办公桌了
很多小可爱可能已经想到法子了,数据丢失不就是因为一个机子坏了吗,我多在线上弄几个机子不就好了,有时为了避免灾难性影响,我们可以在多个地方部署服务器,比如北京、上海、华盛顿、纽约,地球不爆炸,服务器不会蹦(此时的我竖起了大拇指)
多台服务器就会涉及到数据的迁移复制工作、迁移原理、以及迁移过程中需要注意的问题等,接下来肝~
了解主从复制
概述
主从复制,指的是将一台Redis服务器的数据复制到另一台Redis服务器,数据来源称为主节点master,数据接受者称为从节点slave;数据的复制是单向的,数据只能从主节点复制到从节点,反之不行
作用
简单概括下主从复制的作用:
- 数据备份:主从复制实现了数据的热备份,是一种数据冗余方式,当主节点出现问题,可以通过从节点来提供服务,实现故障快速恢复
- 负载均衡:有了主从复制,可以通过读写分离的方式来方式来负载均衡,由主节点提供写服务,从节点提供读服务,来分担服务器的压力,尤其是当读多写少的情况,可以通过增加读服务器的方式来实现增加并发量
主从复制还是哨兵模式和集群模式的实现基础
主从复制原理
我们了解了主从复制的用途,接下来要说的就是大家最“爱”读的原理了,主从复制大致可以分为三个过程:建立连接阶段、数据同步阶段、命令传播阶段
建立连接:
顾名思义,建立连接就是由主从服务器之间建立连接,为数据同步做好准备。(你和你女朋友大尅四之前,不先牵个手沟通一下子感情?)
从节点服务器内部维护了masterhost、masterport两个字段,用于保存主节点服务器的IP和端口信息
建立连接有以下几个步骤:
- 保存主节点信息:从节点完成对主节点信息的保存之后,复制操作在这之后进行
- 建立socket连接:从节点每秒一次调用复制定时函数replicationCron(),如果发现主节点可用,根据ip和port创建socket连接
- 检查是否可用:从节点成为主节点的客户端之后发送ping命令,检查socket连接是否可用,以及主节点当前是否能够处理请求;
- 身份验证:如果从节点设置了masterauth,则从节点需要向主节点进行身份验证,从节点身份验证是通过auth命令完成,auth命令参数即为masterauth的值
- 主节点保存从节点信息:身份验证之后从节点向主节点发送其监听的端口号,主节点将该信息保存到从节点对应客户端的slave_listening_port字段中第三步中检查是否可用阶段,发送ping命令之后可能会出现三种情况
(1)返回pong:socket正常,连接可用,复制过程继续
(2)超时:一定时间从节点未收到回复,则socket不可用,断开连接重新尝试
(3)返回pong以外的结果:如主节点返回其它结果(正在处理超时运行的脚本)则此时主节点无法处理命令,从节点断开socket连接并重新尝试
数据同步:
主从服务器建立连接之后,开始数据的同步,也是整个复制最核心的阶段,也可以理解为从服务器的数据初始化过程,具体执行方式是:从节点向主节点发送psync命令(在Redis2.8之前是sync命令)
在数据同步阶段之前,从节点是主节点的客户端,主节点不是从节点的客户端,而这一阶段之后主从节点互为客户端;(原因是在之前主节点只需要响应从节点请求,不需要发送请求,而在这个阶段及之后,主节点需要主动向从节点发送请求,如推送缓冲区中的写命令)
数据同步根据主从节点的状态分为全量复制和部分复制,在Redis2.8之前从节点和主节点之间通过sync命令进行全量复制,Redis2.8之后,从节点向主节点发送psync命令同步数据,则根据主从节点状态决定复制方式可能是全量复制或者部分复制
来一起学习下吧
- 全量复制:用于初次复制和其它无法进行部分复制的情况,将主节点所有数据发送给从节点是一个很重型的工作
- 部分复制:用于网络中断等情况后的复制,只将中断期间主节点执行的写命令发送给从节点,与全量复制相比更加高效。需要注意的是,如果网络中断时间过长,导致主节点没有能够完整地保存中断期间执行的写命令,则无法进行部分复制,仍使用全量复制。
全量复制过程
- 从节点判断无法进行部分复制,向主节点发送全量复制的请求;或从节点发送部分复制的请求,但主节点判断无法进行部分复制(如何判断,文章下面说)
- 全量复制,执行bgsave,在后台生成RDB文件,并使用一个缓冲区(复制缓冲区)记录从现在开始的所有写命令
- 主节点bgsave执行完将RDB文件发送给从节点,从节点首先清除自己的旧数据,然后载入接收的RDB文件,更新数据库状态至主节点执行bgsave时的状态
- 主节点将复制缓冲区中的所有写命令发送给从节点,从节点执行这些写命令,将数据库状态更新到最新状态
如果从节点开启了AOF,则会触发bgrewriteaof的执行,从而保证AOF文件更新至主节点的最新状态
全量复制是很重型的操作,主节点通过bgsave命令fork子进程进行RDB持久化,很耗CPU,主节点再通过网络将RDB文件发送到从节点,也很消耗带宽,从节点在清除老数据和载入新数据的整个过程是阻塞的,无法响应客户端的命令
部分复制过程
全量复制在主节点数据量较大时效率太低,因此Redis2.8提供部分复制,主要用于网络中断时的数据同步,了解部分复制过程之前先学习三个概念:
(1)复制偏移量
主节点和从节点分别维护一个复制偏移量offset,代表的是主节点向从节点传递的字节数,主节点每次向从节点传递N个字节数,主节点的offset增加N,从节点每次从主节点收到N个字节数,从节点增加N
offset用于判断主从节点的状态是否一致,相同则代表一致,不同则代表不一致,两者之差也就是从节点缺少的那部分数据
(2)复制积压缓冲区
复制积压缓冲区是由主节点维护的、固定长度的、先进先出(FIFO)队列,默认大小1MB;当主节点开始有从节点时创建,其作用是备份主节点最近发送给从节点的数据,无论有一个还是多个从节点,主节点只需要一个复制积压缓冲区
在命令传播阶段,主节点除了将写命令发送给从节点,还会发送一份给复制积压缓冲区,作为写命令的备份;除了存储写命令,复制积压缓冲区中还存储了其中的每个字节对应的复制偏移量(offset)
复制积压缓冲区就是为了弥补上面所说的偏移量offset的,如果主从节点offset差距过大超过缓冲区长度时,此时无法进行部分复制,只能全量复制。为了提高网络中断时部分复制执行的概率,可以增大复制积压缓冲区的大小(通过配置repl-backlog-size);
例如如果网络中断的平均时间是20s,而主节点平均每秒产生的写命令(特定协议格式)所占的字节数为100KB,则复制积压缓冲区的平均需求为2MB,保险起见,可以设置为5MB,来保证绝大多数断线情况都可以使用部分复制
从节点将offset发送给主节点之后,主节点根据offset和缓冲区大小决定能否进行部分复制
(3)服务器运行ID
每个Redis节点(无论主从),在启动时都会自动生成一个随机ID(每次启动都不一样),由40个随机的十六进制字符组成;runid用来唯一识别一个Redis节点,通过info Server命令,可以查看节点的runid
主从节点初次复制时,主节点将自己的runid发送给从节点,从节点将这个runid保存起来;当断线重连时,从节点会将这个runid发送给主节点;主节点根据runid判断能否进行部分复制
关于数据同步阶段的psync命令
(1)首先,从节点根据当前状态,决定如何调用psync命令,如果从节点之前未执行过slaveof或最近执行了slaveof no one,则从节点发送命令为psync ? -1,向主节点请求全量复制。
如果从节点之前执行了slaveof,则发送命令为psync ,其中runid为上次复制的主节点的runid,offset为上次复制截止时从节点保存的复制偏移量。
(2)主节点根据收到的psync命令,及当前服务器状态,决定执行全量复制还是部分复制。
如果主节点版本低于Redis2.8,则返回-ERR回复,此时从节点重新发送sync命令执行全量复制。
如果主节点版本够新,且runid与从节点发送的runid相同,且从节点发送的offset之后的数据在复制积压缓冲区中都存在,则回复+CONTINUE,表示将进行部分复制,从节点等待主节点发送其缺少的数据即可。
如果主节点版本够新,但是runid与从节点发送的runid不同,或从节点发送的offset之后的数据已不在复制积压缓冲区中(在队列中被挤出了),则回复+FULLRESYNC ,表示要进行全量复制,其中runid表示主节点当前的runid,offset表示主节点当前的offset,从节点保存这两个值,以备使用。
命令传播阶段:
数据同步阶段完成之后,主从节点进入命令传播阶段,在这个阶段主节点将自己执行的写命令发送给从节点,从节点接收命令并执行,从而保证主从节点数据的一致性。
需要注意的是,命令传播是异步的过程,即主节点发送写命令后并不会等待从节点的回复;因此实际上主从节点之间很难保持实时的一致性,延迟在所难免。数据不一致的程度,与主从节点之间的网络状况、主节点写命令的执行频率、以及主节点中的repl-disable-tcp-nodelay配置等有关。