Redis 主从复制

1.主从复制的概念

主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(master),后者称为从节点(slave),数据的复制是单向的,只能由主节点到从节点。

默认情况下,每台Redis服务器都是主节点;且一个主节点可以有多个从节点(或没有从节点),但一个从节点只能有一个主节点。

一句话:就是主从复制,master以写为主,slave以读为主,当master数据变化的时候,自动将新的数据异步同步到其他的slave数据库

2.主从复制的作用

  • 数据冗余 :主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。

  • 故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余

  • 负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量

  • 读写分离:可以用于实现读写分离,主库写、从库读,读写分离不仅可以提高服务器的负载能力,同时可根据需求的变化,改变从库的数量;

  • 高可用基石:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础。

3主从复制的原理

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

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

  • 保存主节点(master)信息

  • 从节点(slave)内部通过每秒运行的定时任务维护复制相关逻辑,当定时任务发现存在新的主节点后,会尝试与该节点建立网络连接,如果从节点无法建立连接,定时任务会无限重试直到连接成功或者执行 slaveof no one 取消复制,关于连接失败,可以在从节点执行 info replication 查看 master_link_down_since_seconds 指标,它会记录与主节点连接失败的系统时间。从节点连接主节点失败时也会每秒日志

  • 发送 ping 命令,连接建立成功后从节点发送 ping 请求进行首次通信,如果发送 ping 命令后,从节点没有收到主节点的 pong 回复或者超时,比如网络超时或者主节点正在阻塞无法响应命令,从节点会断开复制连接,下次定时任务会发起重连

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

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

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

4主从复制启用

配从(库) 不配主(库)

密码验证

master如果配置了requirepass参数,需要密码登录 ,那么slave就要配置masterauth来设置校验密码,否则的话master会拒绝slave的访问请求

从节点开启主从复制,有3种方式:

  • 修改配置文件启动

    复制代码
     slaveof <masterip> <masterport>    旧版本                            
    ​
     replicaof <masterip> <masterport>   新版本 
  • 启动命令增加参数启动

    复制代码
    redis-server启动命令后加入 --slaveof <masterip> <masterport> 
  • 客户端启动后输入命令:

    每次与master断开之后,都需要重新连接,除非你配置进了redis.conf文件;在运行期间修改slave节点的信息,如果该数据库已经是某个主数据库的从数据库,那么会停止和原主数据库的同步关系转而和新的主数据库同步,重新拜码头

    复制代码
    slaveof <masterip> <masterport>
  • 查看复制节点的主从关系和配置信息

    复制代码
    info replication
  • 停止与其他数据库的同步

    复制代码
    slaveof no one

    5.主从复制的常用相关配置

    从数据库配置

  • slaveof/replicaof <masterip> <masterport>: slave实例需要配置该项,指向master的(ip, port)

  • masterauth <master-password>: 如果master实例启用了密码保护,则该配置项需填master的启动密码;若master未启用密码,该配置项需要注释掉

  • slave-serve-stale-data/replica-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/replica-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

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

6.主从复制进阶常见问题解决

6.1 读写分离

读流量分摊到从节点。这是个非常好的特性,如果一个业务只需要读数据,那么我们只需要连一台 slave 从机读数据。虽然读写有优势,能够让读这部分分配给各个 slave 从机,如果不够,直接加 slave 机器就好了。但是也会出现以下问题

  • 复制数据延迟

    可能会出现 slave 延迟导致读写不一致等问题,当然你也可以使用监控偏移量 offset,如果 offset 超出范围就切换到 master 上,逻辑切换,而具体延迟多少,可以通过 info replication 的 offset 指标进行排查。

    对于无法容忍大量延迟场景,可以编写外部监控程序监听主从节点的复制偏移量,当延迟较大时触发报警或者通知客户端避免读取延迟过高的从节点

    同时从节点的slave-serve-stale-data参数也与此有关,它控制这种情况下从节点的表现:如果为yes(默认值),则从节点仍能够响应客户端的命令;如果为no,则从节点只能响应info、slaveof等少数命令。该参数的设置与应用对数据一致性的要求有关;如果对数据一致性要求很高,则应设置为no。

  • 从节点故障问题

    对于从节点的故障问题,需要在客户端维护一个可用从节点可用列表,当从节点故障时,立刻切换到其他从节点或主节点,之后讲解redis Cluster 时候可以解决这个问题

6.2配置不一致

主机和从机不同,经常导致主机和从机的配置不同,并带来问题

主机和从机有时候会发生配置不一致的情况,例如 maxmemory 不一致,如果主机配置 maxmemory 为8G,从机 slave 设置为4G,这个时候是可以用的,而且还不会报错。但是如果要做高可用,让从节点变成主节点的时候,就会发现数据已经丢失了,而且无法挽回

6.3规避全量复制

全量复制指的是当 slave 从机断掉并重启后,runid 产生变化而导致需要在 master 主机里拷贝全部数据。这种拷贝全部数据的过程非常耗资源。

全量复制是不可避免的,例如第一次的全量复制是不可避免的,这时我们需要选择小主节点,且maxmemory 值不要过大,这样就会比较快。同时选择在低峰值的时候做全量复制。

造成全量复制的原因

  • 主从机的运行 runid 不匹配 主节点如果重启,runid 将会发生变化。如果从节点监控到 runid 不是同一个,它就会认为你的节点不安全。当发生故障转移的时候,如果主节点发生故障,那么从机就会变成主节点。我们会在后面讲解哨兵和集群

  • 复制缓冲区空间不足 比如默认值1M,可以部分复制。但如果缓存区不够大的话,首先需要网络中断,部分复制就无法满足。其次需要增大复制缓冲区配置(relbacklogsize),对网络的缓冲增强。为了解决这个问题,Redis提供了debug reload的重启方式:重启后,主节点的runid和offset都不受影响,避免了全量复制

  • 复制风暴 当一个主机下面挂了很多个 slave 从机的时候,主机 master 挂了,这时 master 主机重启后,因为 runid 发生了变化,所有的 slave 从机都要做一次全量复制。这将引起单节点和单机器的复制风暴,开销会非常大,可以采用树状结构降低多个从节点对主节点的消耗

6.4单机器的复制风暴

由于 Redis 的单线程架构,通常单台机器会部署多个 Redis 实例。当一台机器(machine)上同时部署多个主节点(master)时,如果每个 master 主机只有一台 slave 从机,那么当机器宕机以后,会产生大量全量复制。这种情况是非常危险的情况,带宽马上会被占用,会导致不可用。

应该把主节点尽量分散在多台机器上,避免在单台机器上部署过多的主节点。 当主节点所在机器故障后提供故障转移机制,避免机器恢复后进行密集的全量复制

6.5 主机宕机后 从机不会自动上位

默认情况下,不会在slave节点中自动选一个master,需要人工干预

7全量复制和部分复制

7.1全量复制和部分复制的概念

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

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

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

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

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

偏移量的作用非常大,它是用来衡量主从节点数据是否一致的唯一标准,如果主从节点的 offset 相等,表明数据一直,否则表明数据不一致。在不一致的情况下,可以根据两个节点的 offset 找出从节点的缺少的那部分数据。比如,主节点的 offset 是 500,从节点的 offset 是 400,那么主节点在进行数据传输时只需要将 401 ~ 500 传递给从节点即可,这就是部分复制。 ​

复制积压缓冲区 :复制积压缓冲区是保存在主节点上的一个固定长度的队列,先进先出 ,配置参数为: repl-backlog-size, 默认大小为1MB,当主节点有连接的从节点(slave)时被创建,这时主节点(master)响应写命令时,不但会把命令发送给从节点,还会写入复制积压缓冲区。

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

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

服务器运行ID(runid):每个Redis节点(无论主从),在启动时都会自动生成一个随机ID(每次启动都不一样),由40个随机的十六进制字符组成;runid用来唯一识别一个Redis节点。

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

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

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

7.2全量复制流程

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

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

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

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

  • 主机send RDB 发送 RDB 文件给从机 从节点接收后保存在本地直接将其作为数据文件,如果从节点本地有 RDB 文件,则从节点会先清空 RDB 文件, 如果从节点还开启了 AOF,则还会进行 AOF 重写

  • 发送缓冲区数据

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

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

造成全量复制的原因

  • 第一次建立主从连接进行数据同步是全量复制 不可避免

  • 从节点发送 psync {runid} {offset} 时,runid 与当前主节点的 runid 不匹配则进行全量复制

  • 从节点所需要同步数据的偏移量 offset 不在复制积压缓冲区中,也会进行全量复制

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

bgsave 时间

RDB 文件网络传输时间

从节点清空数据的时间

从节点加载 RDB 的时间

7.3部分复制流程

  • 如果网络抖动(连接断开 connection lost)如果超过了 repl-timeout 时间,主节点会认为从节点出现故障不可达

  • 主机master 还是会写 replbackbuffer(复制缓冲区)默认 1MB

  • 从机slave 会继续尝试连接主机

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

  • 如果 master 发现你的偏移量是在缓冲区的范围内,就会返回 continue 命令,进行部分复制,否则进行全量复制

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

7.4 redis是如何决定是全量复制还是部分复制

  • 从节点将offset发送给主节点后,主节点根据offset和缓冲区大小决定能否执行部分复制:

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

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

  • 比较runid

当redis主从一方重启后,会比较runid 如果一致就会尝试部分复制,不一致就全量复制

8实战主从复制 -- 配置文件

8.1 虚拟机准备

角色 IP 端口
主服务器 192.168.211.128 6379
从服务器 192.168.211.129 6379
从服务器 192.168.211.130 6379

一主二仆

  • 可以使用配置文件固定写死主从关系

    从机不能执行写命令 只能读

    从机首次连接是全量复制,后续是增量复制

    主机shutdown后 从机不动,原地待命,从机数据可以正常使用,等待主机重启归来

    主机shutdown后 主从关系依然存在,从机依旧是从机,主机回来后可以顺利复制

    某台从机down后,master继续,从机重启后依据偏移量继续复制 此时需要分析情况,有的情况需要全量复制

  • 可以命使用令操作手动主从关系指令 salveof 主库IP 主库端口

用命令使用的话,从机重启后 主从关系就消失了

配置 VS 命令的区别,配置,持久稳定永久生效;命令,当成生效

薪火相传

  • 上一个slave可以是下一个slave的master,slave同样可以接收其他slaves的连接和同步请求,那么该slave作为了链条中下一个的master,可以有效减轻主master的写压力

  • 中途变更转向:会清除之前的数据,重新建立主从关系并拷贝最新的

  • slaveof 新主库IP 新主库端口

作为主机的slaves依旧没有写命令的权限

反客为主

slaveof no one 使当前数据库停止与其他数据库的同步关系

8.2 主服务器配置文件

复制代码
# 允许所有IP访问  也可以注释掉
bind 0.0.0.0

# 端口号
port 6379

# 开启保护模式  需要密码访问  bind注释后 可以设置no
protected-mode yes

#日志
logfile "6379.log"

# 日志级别  调试:debug 生产:notice
loglevel  debug

# 日志路径
dir "/"

# 设置访问密码
requirepass xxx


# 设置主从复制密码  故障迁移时使用
# redis auth与主从复制密码需要一致
masterauth xxx

8.3 从服务器配置文件

复制代码
# 允许所有IP访问
bind 0.0.0.0

# 端口号
port 6379

#日志
logfile "6379.log"

# 日志级别  调试:debug 生产:notice
loglevel  debug 

#日志路径
dir "./"

# 开启保护模式  需要密码访问
protected-mode yes

# 识别主机
replicaof 192.168.1.75 6379

# 主从复制密码 与主机一致
masterauth xxx

# 设置访问密码  搭建哨兵时 主从密码配置一样
requirepass xxx

8.4 主从测试

复制代码
# 主服务器
# 重启redis
127.0.0.1:6379> ./redis-server ./redis.conf

# 登录redis
127.0.0.1:6379> ./redis-cli

# 验证密码
127.0.0.1:6379> auth xxx

# 查看复制信息
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=192.168.1.76,port=6379,state=online,offset=70,lag=0
slave1:ip=192.168.1.77,port=6379,state=online,offset=70,lag=1
master_replid:5861fb1a2a9792819e65791764f401cec0275d78
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:70
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:70

# 从服务器查看复制信息
127.0.0.1:6379> info replication
# Replication
role:slave
master_host:192.168.1.75
master_port:6379
master_link_status:up
master_last_io_seconds_ago:0
master_sync_in_progress:0
slave_repl_offset:28
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:5861fb1a2a9792819e65791764f401cec0275d78
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:28
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:28

8.5 断开主从配置或重新配置

复制代码
# 客户端执行
slaveof no one

# 关闭redis
shutdown

#重启redis 或进行配置
相关推荐
好奇的菜鸟1 小时前
如何在IntelliJ IDEA中设置数据库连接全局共享
java·数据库·intellij-idea
巴伦是只猫2 小时前
【机器学习笔记Ⅰ】13 正则化代价函数
人工智能·笔记·机器学习
wuk9982 小时前
基于MATLAB编制的锂离子电池伪二维模型
linux·windows·github
DuelCode2 小时前
Windows VMWare Centos Docker部署Springboot 应用实现文件上传返回文件http链接
java·spring boot·mysql·nginx·docker·centos·mybatis
优创学社22 小时前
基于springboot的社区生鲜团购系统
java·spring boot·后端
幽络源小助理3 小时前
SpringBoot基于Mysql的商业辅助决策系统设计与实现
java·vue.js·spring boot·后端·mysql·spring
猴哥源码3 小时前
基于Java+springboot 的车险理赔信息管理系统
java·spring boot
Hello.Reader3 小时前
Redis 延迟排查与优化全攻略
数据库·redis·缓存
YuTaoShao3 小时前
【LeetCode 热题 100】48. 旋转图像——转置+水平翻转
java·算法·leetcode·职场和发展
ai小鬼头4 小时前
AIStarter如何助力用户与创作者?Stable Diffusion一键管理教程!
后端·架构·github