文章目录
- 一、Redis集群概述
- 二、Redis多节点的启用
- 1)启动的方式
- 2)演示
- 3)传输延迟
- 三、拓扑结构
- 1)一主一从
- 2)一主多从
- 3)树形主从
- 四、主从结点数据复制的流程&原理
- 1)流程
- 2)psync数据同步
- 概述
- psync语法
- 主从节点信息介绍
- 3)全量复制流程
- 4)增量复制流程
- 5)实时复制
- 五、哨兵
- 1)概述
- 2)原理
- 第一步 选举leader
- 第二步 选择主节点
一、Redis集群概述
在分布式系统中,通常会把数据存放到多个副本中,并且部署到多个服务器,以此来满足故障恢复和负载均衡的需求。Redis也是如此,它可以实现多个结点。每个结点可以存储相同的数据。结点集合中又可以分为一个主结点和多个从结点。客户端可以在主节点和从节点读数据,但是写数据只能在主结点上写。主节点写入新数据,则通过本节要讲的主从复制机制,把数据同步给从结点。
每个从结点只能有一个主节点,而一个主节点可以同时具有多个从结点。复制的数据流是单向的,只能由主节点到从节点:
二、Redis多节点的启用
1)启动的方式
启动多个Redis结点的方式有以下三种:
- 在配置文件中加入
slaveof {masterHost} {masterPort}
随 Redis 启动生效(建议使用,重启之后仍然生效)。 - 在
redis-server
启动命令时加入--slaveof {masterHost} {masterPort}
生效(重启后不会生效)。 - 直接使用 redis 命令:
slaveof {masterHost} {masterPort}
生效(重启后不会生效)。
2)演示
接下来,我们将 redis.conf 配置文件复制一份 redis-slave.conf,并且修改其 daemonize 为 yes。
创建一个主节点以及两个从节点为例:
1 . 创建配置文件
创建一个文件夹存储不同redis结点的配置文件(把主节点的配置文件复制过来重新起名字即可):
2. 修改新的配置文件
slave1.conf 和slave2.conf配置文件中的端口号改成可用端口号(不和其他进程冲突即可)
daemonize选项改成yes,表示后台进程运行。
此外配置结点的从属关系(slave1和slave2作为6379端口结点的从节点):
3. 启动三个结点(启动后进行配置,也需要重新启动)
准备工作完之后,我们在6379主节点插入一个数据:
发现从节点6380也会收到:
这说明主节点把数据复制给了从节点。
如果从节点想要断开与主节点的连接,只需要在从节点中执行slaveof no one
即可:
注意:
- redis如果使用server redis-server start 的命令启动,直接kill这个redis结点,它会自动启动,如果需要关闭这个节点,需要使用server redis-server stop命令来结束。
- 如果主节点主动断开与从节点的连接,那么从节点之间会通过
哨兵机制
,重新选举出一个从节点作为主节点。
可以通过info replication
查看从结点复制信息:
通过 slaveof 命令还可以实现切主操作,将当前从节点的数据源切换到另⼀个主节点。执⾏
slaveof {newMasterIp} {newMasterPort} 命令即可。
切主操作主要流程:
1)断开与旧主节点复制关系。
2)与新主节点建⽴复制关系。
3)删除从节点当前所有数据。
4)从新主节点进⾏复制操作。
3)传输延迟
主从节点之间一般是部署到不同的机房中的,因此数据传输的延迟是必须考虑的一个因素。redis给我们提供了一个配置选项:repl-disable-tcp-nodelay
默认值为no,表示开启TCP_NODELAY(这里没有打错,就是开启,no可以理解为延迟发送):
-
当关闭时,主节点产生的命令数据无论大小都会及时地发送给从节点,这样主从之间延迟会变小,但增加了网络带宽的消耗。适用于主从之间的网络环境良好的场景,如同机房部署。
-
当开启时,主节点会合并较小的 TCP 数据包从而节省带宽(类似于捎带应答)。默认发送时间间隔取决于 Linux 的内核,一般默认为 40 毫秒。这种配置节省了带宽但增大主从之间的延迟。适用于主从网络环境复杂的场景,如跨机房部署。
三、拓扑结构
1)一主一从
最简单的结构,⽤于主节点出现宕机时从节点提供故障转移⽀持(直接变成主节点)
如图所⽰。当应⽤写命令并发量较⾼且需要持久化时,可以只在从节上开启 AOF,这样既可以保证数据安全性同时也避免了持久化对主节点的性能⼲扰。
需要注意的是,如果主节点宕机,要避免其重启,导致数据内容出现过时错误。
2)一主多从
对于读命令大的场场景,这种结构非常适合,客户端不同都从主节点读取数据,可以把客户端的请求流分散给其他的从节点,实现负载均衡。
3)树形主从
在一主多从的结构中,如果读的请求过大,从节点设置过多,这对主节点的性能开销也是非常大的。因为他需要把自己的数据复制到多个结点,此外还有处理读写请求。
树形主从结构通过层级划分,把数据复制的压力分摊到了从节点集合中,一定程度上降低了主节点的性能开销(减少主节点的数据复制传输次数)
四、主从结点数据复制的流程&原理
1)流程
2)psync数据同步
概述
psync命令专门用于主从复制。
它可以实现两种复制方式:
- 全量复制: 一般用于初次数据同步,早期redis版本只支持这种赋值方式。他会把主节点的全部数据发送给从节点,当主节点数据量过大,这个操作开销会非常大
- 增量复制: 用于从节点因为一些特殊情况宕机,重启之后,只需要让主节点补发部分宕机时的数据遗漏的数据即可。因为只需要补发少量数据,因此开销较小。
从节点会发送当前从节点的replid 和offset,根据这个信息,主节点判断进行全量复制(FULLRESYNC)还是增量复制(CONTINEU)。遇到非预期情况返回ERR,表示拒绝请求。
注意:
redis主节点中有一个全局积压缓冲区。具体进行全量复制还是增量复制,取决于这个全局缓冲区数据是否已经达到上线,如果达到上线,进行全量复制。反之直接通过全局积压缓冲区的数据同步给从节点即可。
psync语法
psync replicationid offset
- 如果replicationid为? 并且offset为-1 那么进行全量复制
- 如果replicationid和offset设置为具体的值,那么进行增量复制
- replicationid用于表示每一个集群,由主节点自动生成,可以同步给从节点。
- offset是一串数字,用来表示从节点与主节点的同步情况。比如主节点offset=100,从节点offset=50。那么从节点还有50量的数据没有从主节点同步过来。如果主从节点offset一样,那么他们俩数据一定是一致的(随着新的数据写入主节点,主节点的offset也是会增加的)
主从节点信息介绍
通过
info replication
可以查看主从节点信息的:
-
role:slave
表示当前 Redis 实例的角色是从节点(slave),主节点(master)。 -
master_host:127.0.0.1
表示主节点的ip地址 -
master_port:6379
主节点的端口号。 -
master_link_status:up
主从连接状态。up
表示当前从节点与主节点的连接正常。如果是down
,则表示连接断开。 -
master_last_io_seconds_ago:5
距离上次与主节点进行数据交互的时间(以秒为单位)。这里是5
秒,说明 5 秒前从节点与主节点有数据交互,连接是活跃的。 -
master_sync_in_progress:0
是否正在进行主从同步。0
表示没有正在进行同步(即同步已完成或未启动)。如果是1
,则表示正在进行全量同步。 -
slave_repl_offset:63188
从节点的复制偏移量(replication offset)。主节点和从节点的偏移量应该接近,如果差距很大,可能表示同步延迟。 -
slave_priority:100
从节点的优先级,用于故障转移(failover)。默认值是100
。如果主节点宕机,优先级更高的从节点(值越小)更有可能被选为主节点。 -
slave_read_only:1
从节点是否为只读模式。1
表示从节点是只读的(Redis 默认设置),不能处理写请求,只能处理读请求。 -
connected_slaves:0
当前从节点连接的下级从节点数量。这里是0
,表示这个从节点没有其他从节点连接到它。 -
repl_backlog_active:1
复制积压缓冲区(replication backlog)是否激活。1
表示已激活。积压缓冲区用于支持部分重同步(partial resync),当从节点短暂断开后,可以通过缓冲区快速恢复同步,而不必进行全量复制减少开销。 -
repl_backlog_size:1048576
复制积压缓冲区的大小(以字节为单位)。这里是1048576
字节,即 1MB。这是主节点用来存储最近写入操作的缓冲区大小。 -
repl_backlog_first_byte_offset:1
积压缓冲区中第一个字节的偏移量。这里是1
,表示缓冲区从偏移量1
开始存储数据。 -
repl_backlog_histlen:63188
积压缓冲区中存储的历史数据长度(以字节为单位)。这里是63188
字节,说明缓冲区中当前存储了 63188 字节的复制数据。 -
如图:
master_replid和master_replid2都是用来表示主节点(或者说当前所属主节点)当从节点认为主节点宕机后,从节点会选举出一个新的主节点,新的主节点的replid赋值给master_replid,旧主节点的replid复制给master_replid2。
当旧的主节点恢复后,从节点可以感知到,进而重新回到原来的主结点的怀抱。
secod_repl_offset用法是类似的,保存旧结点的数据偏移量。
3)全量复制流程
读取中...
- 从节点发送psync ? -1表示请求全量复制
- 主节点进行判断同意后,返回FULLRESYNC响应
- 从节点保存主节点的一些必要信息
- 主节点启动RDB快照(使用bgsave命令),生成RDB文件(在硬盘)
- 主节点把这个RDB文件发送给从节点。
- 主节点把创建RDB和发送RDB文件给从节点期间的新数据流存储到了全局积压缓冲区,一旦从节点解析完成了RDB文件,从节点会把缓冲区的数据一并通过RDB格式发送给从节点,以此确保数据一致性。
- 从节点清空自己的RDB文件,用于接收新的RDB文件。
- 从节点加载 RDB ⽂件得到与主节点⼀致的数据。
- 如果从节点加载 RDB 完成之后,并且开启了 AOF 持久化功能,它会进⾏ bgrewrite 操作,得到最近的 AOF ⽂件。
通过上面流程分析我们可以知道,全量复制是一个重量操作,主节点需要bgsave(fork创建子进程),把所有数据写入到硬盘,然后把硬盘中的数据网络传输给从节点。因此非必要,建议不要进行全量复制。
无磁盘复制
默认情况下, 进⾏全量复制需要主节点⽣成 RDB ⽂件到主节点的磁盘中, 再把磁盘上的 RDB
⽂件通过发送给从节点.
Redis 从 2.8.18 版本开始⽀持⽆磁盘复制. 主节点在执⾏ RDB ⽣成流程时, 不会⽣成 RDB ⽂
件到磁盘中了, ⽽是直接把⽣成的 RDB 数据通过⽹络发送给从节点. 这样就节省了⼀系列的写
硬盘和读硬盘的操作开销
4)增量复制流程
实际上就是部分复制。
读取中...
在上文我们就已经提到了一个概念,叫全局积压缓冲区
。全局意思是,如果主节点从属的任意一个子节点短暂宕机,进行部分复制,直接在这个缓冲区拿即可。
- 双向箭头表示主从结点连接断开。
- 连接断开期间,一直有新的数据写入到主节点
- 表示从结点宕机恢复,主动与主结点连接
- 从节点发送自己的replid和offset给主节点
- 如果主节点判断replid与自己一直并且主节点offset与从节点offset之差没要超过缓冲区存储数据大小,返回+CONTINUE给从节点
- 主节点把缓冲区部分数据同步给从节点。
5)实时复制
当主从节点数据基本同步后,开启实时复制。其大致原理为主从节点开启一个TCP长连接。主节点有数据写入,那么就源源不断地把数据发送给从节点。
- 主从节点彼此都有⼼跳检测机制,各⾃模拟成对⽅的客⼾端进⾏通信。
- 主节点默认每隔 10 秒对从节点发送 ping 命令,判断从节点的存活性和连接状态。
- 从节点默认每隔 1 秒向主节点发送 replconf ack {offset} 命令,给主节点上报⾃⾝当前的复制偏移量。
注意
如果主节点发现从节点通信延迟超过 repl-timeout 配置的值(默认 60 秒),则判定从节点下线,断开复制客⼾端连接。从节点恢复连接后,⼼跳机制继续进⾏
五、哨兵
1)概述
前文中我们提到如果主节点挂了,Redis可以采用某些机制在其他从节点中选取一个从节点作为主节点,这样做可以保证集群的可靠性,避免因为主节点确实导致无法进行写操作。
那么它是如何实现的呢? 答案就是哨兵机制。
实际上这个机制类似于RabbitMQ中的仲裁队列,同样采用了数据一致性算法——Raft算法。
2)原理
第一步 选举leader
- 每一个哨兵(sentinel)都是一个独立的进程,不断监视主从节点。
如果某个哨兵发现主节点挂了(可能是误判),这种情况就叫主观下线。 - 当半数哨兵发现主节点挂了,这种情况叫客观下线(客观下线,大概率主节点是真的挂了)
- 哨兵集合判主节点客观下线后,会在哨兵集合会进行内部选举,在多个烧饼中选择出一个leader,负责在从节点中选择一个新的主节点。
- 某个哨兵一旦感知到需要进行选举,自己的任期会增加,并且会立马给自己投上一票,然后发送信息给其他哨兵也给自己投上一票
- 倘若别的哨兵没有投票,并且当前哨兵任期不大于自己,那么别的哨兵也会给当前哨兵投上一票。
- 当某个哨兵票数过半,那么他就是leader
第二步 选择主节点
leader根据当前可用从节点按照以下优先级选择主节点:
- 根据从节点中的参数
slave_priority
进行选择,选择最大值的为主节点 - 如果slave_priority相同,根据
offset
选择,选择slave_priority
最大的(保证数据最新) - 如果以上条件都相同,那么就通过runId进行选择(runId每个节点重启后都不一样,因此就是随机选择)
关于raft一致性算法,详细可以查看Raft共识算法动画演示