redis优化系列(二)Redis主从原理、主从常用配置等

内容摘要
该系列博文之上一篇:传送门 走你一、主从复制原理主从复制过程大体可以分为3个阶段:连接建立阶段(即准备阶段)、数据同步阶段、命令传播阶段。在从节点执行 slaveof 命令后,复制
文章正文

该系列博文之上一篇:传送门 走你

一、主从复制原理

主从复制过程大体可以分为3个阶段:连接建立阶段(即准备阶段)、数据同步阶段、命令传播阶段。

在从节点执行 slaveof 命令后,复制过程便开始运作,下面图示大概可以看到,从图中可以看出复制过程大致分为6个过程

123.jpg

主从配置之后的日志记录也可以看出这个流程(看主redis或从redis日志都可以):

vi /var/log/redis/redis.log

r1.png


1)保存主节点(master)信息:

    执行 slaveof 后 Redis 会打印如下日志:

r2.png


2)从节点(slave)底层内部通过每秒运行的定时任务维护复制相关逻辑,当定时任务发现存在新的主节点后,会尝试与该节点建立网络连接


从节点与主节点建立网络连接示例图:

r3.png


从节点会建立一个 socket 套接字,从节点建立了一个端口为51234的套接字,专门用于接受主节点发送的复制命令。从节点连接成功后打印如下日志:

r4.png

如果从节点无法建立连接,定时任务会无限重试直到连接成功或者手动执行 slaveof no one 取消复制(断开主从)

注:对一个从redis节点服务器执行命令 slaveof no one 将使得这个从redis节点服务器关闭复制功能,并从 从redis节点服务器转变回主redis节点服务器,原来同步所得的数据集不会被丢弃。


关于连接失败,可以在从节点执行 info replication 查看 master_link_down_since_seconds 指标,它会记录与主节点连接失败的系统时间。从节点连接主节点失败时也会每秒打印如下日志,方便发现问题:

    # Error condition on socket for SYNC: {socket_error_reason}


3)发送 ping 命令

    连接建立成功后从节点发送 ping 请求进行首次通信,ping 请求主要目的如下:

①、检测主从之间网络套接字是否可用。

②、检测主节点当前是否可接受处理命令。

③、如果发送 ping 命令后,从节点没有收到主节点的 pong 回复或者超时,比如网络超时或者主节点正在阻塞无法响应命令,从节点会断开复制连接,下次定时任务会发起重连。

r5.png

r6.jpg

从节点发送的 ping 命令成功返回,Redis 打印如下日志,并继续后续复制流程:

r7.png


4)权限验证。如果主节点设置了 requirepass 参数,则需要密码验证,从节点必须配置 masterauth 参数保证与主节点相同的密码才能通过验证;如果验证失败复制将终止,从节点重新发起复制流程。


5)同步数据集。主从复制连接正常通信后,对于首次建立复制的场景,主节点会把持有的数据全部发送给从节点,这部分操作是耗时最长的步骤。


6)命令持续复制。当主节点把当前的数据同步给从节点后,便完成了复制的建立流程。接下来主节点会持续地把写命令发送给从节点,保证主从数据一致性。

r8.png


可参考以下书籍(需付费购买):

https://gitbook.cn/gitchat/geekbook/5af939544b544674331bce2e


关于主从原理也可参考以下文章:

https://juejin.im/entry/5b39c05df265da596e4cf399

二、全量复制和部分复制

2.1 相关概念

全量复制:用于初次复制或其它无法进行部分复制的情况,将主节点中的所有数据都发送给从节点,是一个非常重型的操作,当数据量较大时,会对主从节点和网络造成很大的开销


部分复制:用于处理在主从复制中因网络闪断等原因造成的数据丢失场景,当从节点再次连上主节点后,(如果条件允许,后面会说到具体那些条件需要被满足),主节点会补发丢失数据给从节点。因为补发的数据远远小于全量数据,可以有效避免全量复制的过高开销,需要注意的是,如果网络中断时间过长,造成主节点没有能够完整地保存中断期间执行的写命令,则无法进行部分复制,仍使用全量复制


复制偏移量:参与复制的主从节点都会维护自身复制偏移量。主节点(master)在处理完写入命令后,会把命令的字节长度做累加记录,统计信息在 info replication 中的 master_repl_offset 指标中:

提醒:记得进入redis客户端执行该命令

#在从节点的redis中执行以下命令
redis-cli
info replication

截图如下:

r9.png


从节点(slave)每秒钟上报自身的复制偏移量给主节点,因此主节点也会保存从节点的复制偏移量,统计指标如下:

再次提醒:记得进入redis客户端执行 info replication 命令

#在主节点的redis中执行以下命令
redis-cli
info replication

截图如下:

r10.png

从节点在接收到主节点发送的命令后,也会累加记录自身的偏移量。统计信息在 info replication 中的 slave_repl_offset 中


复制积压缓冲区:复制积压缓冲区是保存在主节点上的一个固定长度的队列,默认大小为1MB(可以调整大小,具体调整多大看各自需求业务来调整),当主节点有连接的从节点(slave)时被创建,这时主节点(master)响应写命令时,不但会把命令发送给从节点,还会写入复制积压缓冲区。


在命令传播阶段(也可以说是 同步数据阶段),主节点除了将写命令发送给从节点,还会发送一份给复制积压缓冲区,作为写命令的备份;除了存储写命令,复制积压缓冲区中还存储了其中的每个字节对应的复制偏移量(offset) 。由于复制积压缓冲区定长而且先进先出,所以它保存的是主节点最近执行的写命令;时间较早的写命令会被挤出缓冲区。

r11.jpg


2.2 Redis全量复制的过程如下图所示:

r12.png

如上图所示:

1、Redis 内部会发出一个同步命令,刚开始是 Psync 命令,Psync ? -1表示要求 master 主机同步数据

2、主机会向从机发送 runid 和 offset,因为 slave 并没有对应的 offset,所以是全量复制

3、从机 slave 会保存 主机 master 的基本信息 save masterInfo

4、主节点收到全量复制的命令后,执行 bgsave 命令(异步执行),在后台生成RDB文件(快照文件),并使用一个缓冲区(称为复制缓冲区)记录从现在开始执行的所有写命令

5、主机发送 RDB 文件给从机

6、主机发送缓冲区数据到从机

7、刷新旧的数据,从节点在载入主节点的数据之前要先将老数据清除

8、加载 RDB 文件将数据库状态更新至主节点执行bgsave时的数据库状态和缓冲区数据的加载。


全量复制开销,主要有以下几项:

1、bgsave 时间

2、RDB 文件网络传输时间

3、从节点清空数据的时间

4、从节点加载 RDB 的时间

注:生成一个几GB 十几GB的RDB快照文件 大概是需要2-5分钟的样子


2.3 部分复制(又称 增量复制)

部分复制是 Redis 2.8 以后出现的,之所以要加入部分复制,是因为全量复制会产生很多问题,比如像上面的时间开销大、无法隔离等问题, Redis 希望能够在 master 出现抖动(相当于断开连接)的时候,可以有一些机制将复制的损失降低到最低。


部分复制的过程如下图所示:

r13.png

如上图所示:

1、如果网络抖动(连接断开 connection lost)

2、主机 master 还是会写 replbackbuffer(复制缓冲区)

3、从机 slave 会继续尝试连接主机

4、从机 slave 会把自己当前 runid 和偏移量传输给主机 master,并且执行 pysnc 命令同步

5、如果 master 发现从节点的偏移量是在缓冲区的范围内,就会返回 continue(在指的是继续复制的意思) 命令,如果从节点的偏移量不在缓冲区的范围之内,就不会进行部分复制操作了,这样就意味着它会执行全量复制,所以我们要尽量避免这种情况的发生。

6、同步了 offset 的部分数据,所以部分复制的基础就是偏移量 offset


正常情况下redis是如何决定是全量复制还是部分复制?

从节点将offset(偏移量)发送给主节点后,主节点根据offset(偏移量)和缓冲区大小决定能否执行部分复制;

如果offset偏移量之后的数据,仍然都在复制积压缓冲区里,则执行部分复制;

如果offset偏移量之后的数据已不在复制积压缓冲区中(数据已被挤出),则执行全量复制;


缓冲区大小调节:

由于缓冲区长度固定且有限,因此可以备份的写命令也有限,当主从节点offset的差距过大超过缓冲区长度时,将无法执行部分复制,只能执行全量复制。反过来说,为了提高网络中断时部分复制执行的概率,可以根据需要增大复制积压缓冲区的大小(通过配置repl-backlog-size)来设置;例如如果网络中断的平均时间是60s,而主节点平均每秒产生的写命令(特定协议格式)所占的字节数为100KB,则复制积压缓冲区的平均需求为6MB,保险起见,可以设置为12MB,来保证绝大多数断线情况都可以使用部分复制。


服务器运行ID(runid):

每个Redis节点(无论主从),在启动时都会自动生成一个随机ID(每次启动都不一样),由40个随机的十六进制字符组成;runid用来唯一识别一个Redis节点。 通过info server命令,可以查看节点的runid:

r14.png



主从节点初次复制时,主节点将自己的runid发送给从节点,从节点将这个runid保存起来;当断线重连时,从节点会将这个runid发送给主节点;主节点根据runid判断能否进行部分复制:

    如果从节点保存的runid与主节点现在的runid相同,说明主从节点之前同步过,主节点会继续尝试使用部分复制(到底能不能部分复制还要看offset和复制积压缓冲区的情况)

    如果从节点保存的runid与主节点现在的runid不同,说明从节点在断线前同步的Redis节点并不是当前的主节点,只能进行全量复制。

三、主从复制的常用相关配置

1、从redis节点配置:


①、slaveof <masterip> <masterport>

slave实例需要配置该项,指向master的(ip, port)


②、masterauth  <master-password>

如果master实例启用了密码保护,则该配置项需填master的启动密码,若master未启用密码,该配置项需要注释掉


③、slave-serve-stale-data

指定slave与master连接中断时的动作。默认为yes,表明slave会继续应答来自client的请求,但这些数据可能已经过期(因为连接中断导致无法从master同步)。若配置为no,则slave除正常应答"INFO"和"SLAVEOF"命令外,其余来自客户端的请求命令均会得到"SYNC with master in progress"的应答,直到该slave与master的连接重建成功或该slave被提升为master


④、slave-read-only

指定slave是否只读,默认为yes。若配置为no,这表示slave是可写的,但写的内容在主从同步完成后会被删掉


⑤、repl-disable-tcp-nodelay

指定向slave同步数据时,是否禁用socket的NO_DELAY选项。若配置为yes,则禁用NO_DELAY,则TCP协议栈会合并小包统一发送,这样可以减少主从节点间的包数量并节省带宽,但会增加数据同步到slave的时间。若配置为no,表明启用NO_DELAY,则TCP协议栈不会延迟小包的发送时机,这样数据同步的延时会减少,但需要更大的带宽。通常情况下,应该配置为no以降低同步延时,但在主从节点间网络负载已经很高的情况下,可以配置为yes

注:主从节点进行数据传输是基于tcp协议进行传输的


⑥、slave-priority

指定slave的优先级。在不只1个slave存在的部署环境下,当master宕机时,Redis Sentinel会将priority值最小的slave提升为master。需要注意的是,若该配置项为0,则对应的slave永远不会被Redis Sentinel自动提升为master


代码注释
[!--zhushi--]

作者:喵哥笔记

IDC笔记

学的不仅是技术,更是梦想!