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 或进行配置
相关推荐
miss writer几秒前
Redis分布式锁释放锁是否必须用lua脚本?
redis·分布式·lua
车轮滚滚__2 分钟前
uniapp对接unipush 1.0 ios/android
笔记
禁默42 分钟前
深入浅出:AWT的基本组件及其应用
java·开发语言·界面编程
Cachel wood1 小时前
python round四舍五入和decimal库精确四舍五入
java·linux·前端·数据库·vue.js·python·前端框架
Code哈哈笑1 小时前
【Java 学习】深度剖析Java多态:从向上转型到向下转型,解锁动态绑定的奥秘,让代码更优雅灵活
java·开发语言·学习
gb42152871 小时前
springboot中Jackson库和jsonpath库的区别和联系。
java·spring boot·后端
程序猿进阶1 小时前
深入解析 Spring WebFlux:原理与应用
java·开发语言·后端·spring·面试·架构·springboot
zfoo-framework1 小时前
【jenkins插件】
java
风_流沙1 小时前
java 对ElasticSearch数据库操作封装工具类(对你是否适用嘞)
java·数据库·elasticsearch