关于Redis的主从复制机制详解

艺帆风顺 发布于 2025-04-03 23 次阅读


注:本篇部分知识点来源于【Redis深度历险-核心原理和应用实践】书籍当中!

在了解 Redis 的主从复制之前,让我们先来理解一下现代分布式系统的理论基石——CAP 原理。

一、CAP 原理

CAP 原理就好比分布式领域的牛顿定律,它是分布式存储的理论基石。自打 CAP 的论文发表之后,分布式存储中间件犹如雨后春笋般一个一个涌现出来。理解这个原理其实很简单,本节我们首先对这个原理进行一些简单的讲解。

  • C - Consistent ,一致性:在分布式系统中的所有数据备份,在同一时刻是否同样的值。(等同于所有节点访问同一份最新的数据副本)

  • A - Availability ,可用性:保证每个请求不管成功或者失败都有响应。

  • P - Partition tolerance ,分区容忍性:系统中任意信息的丢失或失败不会影响系统的继续运作

网络分区:分布式系统的节点往往都是分布在不同的机器上进行网络隔离开的,这意味着必然会有网络断开的风险,这个网络断开的场景的专业词汇叫着「网络分区」

网络分区产生的问题: 在网络分区发生时,两个分布式节点之间无法进行通信,我们对一个节点进行的修改操作将无法同步到另外一个节点,所以数据的「一致性」将无法满足,因为两个分布式节点的数据不再保持一致。除非我们牺牲「可用性」,也就是暂停分布式节点服务,在网络分区发生时,不再提供修改数据的功能,直到网络状况完全恢复正常再继续对外提供服务。

一句话概括 CAP 原理就是——网络分区发生时,一致性和可用性两难全。

二、Redis主从同步

1、主从同步是什么能干嘛?

主从: 很多企业都没有使用到 Redis 的集群,但是至少都做了主从。有了主从,当 master 挂掉的时候,运维让从库过来接管,服务就可以继续,否则 master 需要经过数据恢复和重启的过程,这就可能会拖很长的时间,影响线上业务的持续服务。

数据同步: 主从复制也有人才称之为主从同步,其实两个说的是一个东西,主要是为了避免单节点Redis出现故障而导致服务无法使用,所以采用部署多个节点,虽然是多个节点,但是节点之间的数据是始终是保持同步(一样)的数据,要新增key/value都新增kev/value,要删除都删除,可以理解为就是克隆!

主从同步功能也不是必须的,但是只要你使用了 Redis 的持久化功能,就必须认真对待主从复制,它是系统数据安全的基础保障。

主从同步主要有两个特点:

  • 读写分离,性能扩展,降低主服务器的压力

  • 容灾,快速恢复,主机挂掉时,从机变为主机

Redis满足CAP理论当中的AP

Redis 的主从数据是异步同步的,所以分布式的 Redis 系统并不满足「一致性」要求。当客户端在 Redis 的主节点修改了数据后,立即返回,即使在主从网络断开的情况下,主节点依旧可以正常对外提供修改服务,所以 Redis 满足「可用性」。

Redis能够保证最终一致性

从节点会努力追赶主节点,最终从节点的状态会和主节点的状态将保持一致。如果网络断开了,主从节点的数据将会出现大量不一致,一旦网络恢复,从节点会采用多种策略努力追赶上落后的数据,继续尽力保持和主节点一致。

Redis 同步支持主从同步从从同步,从从同步功能是 Redis 后续版本增加的功能,为了减轻主库的同步负担。后面为了描述上的方便,统一理解为主从同步。

主redis挂掉以后情况会如何?从机是上位还是原地待命?

主机挂掉后,从机会待命,小弟还是小弟,会等着大哥恢复,不会篡位。

从挂掉后又恢复了,会继续从主同步数据么?

会的,当从重启之后,会继续将中间缺失的数据同步过来。

可以使用 info Replication 命令查看主从复制信息

2、Redis是如何实现数据同步的?

  1. slave启动成功连接到master后,会给master发送数据同步消息(发送sync命令)

  2. master接收到slave发来的数据同步消息后,把主服务器的数据进行持久化到rdb文件,同时会收集接收到的用于修改数据的命令,master将传rdb文件发送给你slave,完成一次完全同步

  3. 全量复制:而slave服务在接收到master发来的rdb文件后,将其存盘并加载到内存

  4. 增量复制:master继续将收集到的修改命令依次传给slave完成同步,
    但是只要重新连接master,一次完全同步(全量复制)将会被自动执行

上面只是一个整体的大概流程,下面我们将对增量同步以及全量同步进行详细介绍!

2.1.增量同步

Redis 同步的是指令流

  1. 主节点会将那些对自己的状态产生修改性影响的指令记录在本地的内存 buffer 中

  2. 然后异步将 buffer 中的指令同步到从节点

  3. 从节点一边执行同步的指令流来达到和主节点一样的状态,一遍向主节点反馈自己同步到哪里了 (偏移量)。

注意:因为内存的 buffer 是有限的,所以 Redis 主库不能将所有的指令都记录在内存 buffer 中。Redis 的复制内存 buffer 是一个定长的环形数组,如果数组内容满了,就会从头开始覆盖前面的内容。

如果因为网络状况不好,从节点在短时间内无法和主节点进行同步,那么当网络状况恢复时,Redis 的主节点中那些没有同步的指令在 buffer 中有可能已经被后续的指令覆盖掉了,从节点将无法直接通过指令流来进行同步,这个时候就需要用到更加复杂的同步机制 —— 快照同步。

2.2.快照同步

快照同步是一个非常耗费资源的操作:

  1. 它首先需要在主库上进行一次 bgsave 将当前内存的数据全部快照到磁盘文件中

  2. 然后再将快照文件的内容全部传送到从节点。

  3. 从节点将快照文件接受完毕后,立即执行一次全量加载,加载之前先要将当前内存的数据清空。

  4. 加载完毕后通知主节点继续进行增量同步。

在整个快照同步进行的过程中,主节点的复制 buffer 还在不停的往前移动,如果快照同步的时间过长或者复制 buffer 太小,都会导致同步期间的增量指令在复制 buffer 中被覆盖,这样就会导致快照同步完成后无法进行增量复制,然后会再次发起快照同步,如此极有可能会陷入快照同步的死循环。

所以务必配置一个合适的复制 buffer 大小参数,避免快照复制的死循环。

当从节点刚刚加入到集群时,它必须先要进行一次快照同步,同步完成后再继续进行增量同步。

2.3.无盘复制

主节点在进行快照同步时,会进行很重的文件 IO 操作,特别是对于非 SSD 磁盘存储时,快照会对系统的负载产生较大影响。特别是当系统正在进行 AOF 的 fsync 操作时如果发生快照,fsync 将会被推迟执行,这就会严重影响主节点的服务效率。

所以从 Redis 2.8.18 版开始支持无盘复制。所谓无盘复制是指主服务器直接通过套接字将快照内容发送到从节点,生成快照是一个遍历的过程,主节点会一边遍历内存,一边将序列化的内容发送到从节点,从节点还是跟之前一样,先将接收到的内容存储到磁盘文件中,再进行一次性加载。

2.4.通过Wait 指令保证强一致性

    Redis 的复制是异步进行的,wait 指令可以让异步复制变身同步复制,确保系统的强一致性 (不严格)。wait 指令是 Redis3.0 版本以后才出现的。

> set key value
OK
> wait 1 0
(integer) 1

    wait 提供两个参数,第一个参数是从库的数量 N,第二个参数是时间 t,以毫秒为单位。它表示等待 wait 指令之前的所有写操作同步到 N 个从库 (也就是确保 N 个从库的同步没有滞后),最多等待时间 t。如果时间 t=0,表示无限等待直到 N 个从库同步完成达成一致。

    假设此时出现了网络分区,wait 指令第二个参数时间 t=0,主从同步无法继续进行,wait 指令会永远阻塞,Redis 服务器将丧失可用性。

3、搭建Redis 1主2从

3.1.安装Redis

下面我们来配置1主2从的效果,现实中是需要3台机器的,为了方便,我们就在一台Linux机器上来演示,通过不同的端口来区分机器,3台机器的配置

(1)停止redis

假如linux当中安装的有redis,可以先全部停掉,停止所有的redis命令:

ps -ef | grep redis | awk -F" " '{print $2;}' | xargs kill -9

(2)下载redis,然后将redis上传到opt目录

如果没有安装wget请先安装:

cd /opt
yum -y install wget
wget https://download.redis.io/redis-stable.tar.gz

(3)安装redis

要编译 Redis,首先是 tar解压,切换到根目录,然后运行make:

tar -xzvf redis-stable.tar.gz
cd redis-stable
yum install gcc-c++
make install

假如make install报如下异常,直接使用make install MALLOC=libc

安装过后你会在src目录中找到几个 Redis 二进制文件,包括:

  • redis-server:Redis 服务器本身

  • redis-cli:是与 Redis 对话的命令行界面实用程序,俗称客户端。

3.2.创建1主2从配置文件

(1)创建配置文件

我们将三台机器的配置文件都放到/opt/redis-stable/config当中

    mkdir /opt/redis-stable/configcd /opt/redis-stable/configcp /opt/redis-stable/redis.conf /opt/redis-stable/config/

    (2)创建master的配置文件:redis-6379.conf

    touch redis-6379.conf
    vi redis-6379.conf

    编辑为如下内容:

    bind 0.0.0.0:表示允许所有ip地址访问,默认是127.0.0.1,只允许当前主机连接,假如这里要是配置成当前机器的ip或者是127.0.0.1,那么可能会导致在windows当中无法连接Redis。

      #redis.conf是redis原配置文件,内部包含了很多默认的配置,这里使用include将其引用,相当于把redis.conf内容直接贴进来了include /opt/redis-stable/config/redis.confdaemonize yesbind 0.0.0.0#配置密码requirepass 123456dir /opt/redis-stable/config/logfile /opt/redis-stable/config/6379.log#端口port 6379#rdb文件dbfilename dump_6379.rdb#pid文件pidfile /var/run/redis_6379.pid

      (3)创建slave1的配置文件:redis-6380.conf

      内容如下,和上面master的类似,多了后面2行配置,主要是指定master的ip和端口以及认证密码。

      192.168.0.108是我的linux的ip,这里记住请切换成自己的!

        include /opt/redis-stable/config/redis.confdaemonize yesbind 0.0.0.0requirepass 123456dir /opt/redis-stable/config/port 6380dbfilename dump_6380.rdbpidfile /var/run/redis_6380.pidlogfile /opt/redis-stable/config/6380.log#用来指定主机:slaveof 主机ip 端口slaveof 192.168.0.108 6379#主机的密码masterauth 123456

        (4)创建slave2的配置文件:redis-6381.conf

          include /opt/redis-stable/config/redis.confdaemonize yesbind 0.0.0.0requirepass 123456dir /opt/redis-stable/config/port 6381dbfilename dump_6381.rdbpidfile /var/run/redis_6381.pidlogfile /opt/redis-stable/config/6381.log#用来指定主机:slaveof 主机ip 端口slaveof 192.168.0.108 6379#主机的密码masterauth 123456
          3.3.启动Redis

          (1)关闭防火墙

          假如不关闭防火墙,可能会出现windows无法连接我们刚刚在虚拟机安装的redis的情况

          • 设置开机启用防火墙:systemctl enable firewalld.service

          • 设置开机禁用防火墙:systemctl disable firewalld.service

          • 启动防火墙:systemctl start firewalld

          • 关闭防火墙:systemctl stop firewalld

          • 检查防火墙状态:systemctl status firewalld

          (2)启动redis

          启动master

          /opt/redis-stable/src/redis-server /opt/redis-stable/config/redis-6379.conf

          启动slave1

          /opt/redis-stable/src/redis-server /opt/redis-stable/config/redis-6380.conf

          启动slave2

          /opt/redis-stable/src/redis-server /opt/redis-stable/config/redis-6381.conf

          若启动有误,大家好好检查下配置,也可以看日志,3台机器启动会在 /opt/redis-stable/config 目录产生日志,如下

          可以通过ps -ef|grep redis 查看启动的redis

          (3)连接redis

          /opt/redis-stable/src/redis-cli -h 127.0.0.1 -p 6379 -a 123456

          查看主机信息:info Replication

          查询从机slave1的信息

          /opt/redis-stable/src/redis-cli -h 127.0.0.1 -p 6380 -a 123456

          3.4.验证主从同步效果

          在master上面执行下面2个命令

          /opt/redis-stable/src/redis-cli -h 127.0.0.1 -p 6379 -a 123456
            127.0.0.1:6379> flushdbOK127.0.0.1:6379> set name readyOK127.0.0.1:6379> set age 30OK

            到slave1上执行下面命令,可以看出来数据已经同步过来了

              [root@bogon config]# /opt/redis-stable/src/redis-cli -h 127.0.0.1 -p 6380 -a 123456Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.127.0.0.1:6380> mget name age1) "ready"2) "30"

              同样到slave2上也执行一下,效果如下,注意在从机当中是不允许执行新增修改命令的!

                [root@bogon config]# /opt/redis-stable/src/redis-cli -h 127.0.0.1 -p 6381 -a 123456Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.127.0.0.1:6381> mget name age1) "ready"2) "30"127.0.0.1:6381> set aa aa(error) READONLY You can't write against a read only replica.
                3.5.通过命令配置主从关系

                上面演示的就是一主二从,不过采用的都是配置文件的方式,实际上从机可以采用命令的方式配置,下面我们来演示一遍,大家看好了。

                (1)将redis-6380.conf 和 redis-6381.conf的这两行配置给删掉

                  #用来指定主机:slaveof 主机ip 端口slaveof 192.168.0.108 6379#主机的密码masterauth 123456

                  (2)启动三个redis

                  启动master

                  /opt/redis-stable/src/redis-server /opt/redis-stable/config/redis-6379.conf

                  启动slave1

                  /opt/redis-stable/src/redis-server /opt/redis-stable/config/redis-6380.conf

                  启动slave2

                  /opt/redis-stable/src/redis-server /opt/redis-stable/config/redis-6381.conf

                  (3)分别登陆3台机器,查看各自主从信息

                  分别登陆对3个redis,然后用 info replication 命令看下3个的主从信息,如下:

                  /opt/redis-stable/src/redis-cli -h 127.0.0.1 -p 6380 -a 123456
                  info Replication

                  这时候我们已经解除了3个节点的主从关系!

                   

                  (4)配置slave1为master的从库

                    /opt/redis-stable/src/redis-cli -h 127.0.0.1 -p 6380 -a 123456config set masterauth 123456slaveof 127.0.0.1 6379info replication

                    (5)配置slave2为master的从库

                    /opt/redis-stable/src/redis-cli -h 127.0.0.1 -p 6381 -a 123456
                    config set masterauth 123456
                    slaveof 127.0.0.1 6379
                    info replication

                    (6)再来看看master的主从信息

                    /opt/redis-stable/src/redis-cli -h 127.0.0.1 -p 6379 -a 123456
                    info replication

                    注意:通过 slaveof 命令指定主从的方式,slave重启之后主从配置会失效,所以,重启后需要在slave上重新通过 slaveof 命令进行设置,这个不要忘记了。中途通过 slaveof 变更转向,本地的数据会被清除,会从新的master重新同步数据。

                    3.6.反客为主

                    当master挂掉之后,我们可以从slave中选择一个作为主机。比如我们想让slave1作为主机,那么可以在slave1上执行下面的命令就可以了。

                    slaveof no one

                    这里我通过ps-ef|grep redis查询出来了6379主节点的进程,然后直接给强制kill关闭了

                    此时slave1就变成主机了,然后再去其他slave上面执行 slaveof 命令将其挂在slave1上。

                    配置slave2为slave1的从库

                      /opt/redis-stable/src/redis-cli -h 127.0.0.1 -p 6381 -a 123456config set masterauth 123456slaveof 127.0.0.1 6380info replication

                      假如过了一段时间master恢复了,如果还想让他当主,还得将所有redis重启一遍才能让他继续当主。

                      4、消息丢失

                      Redis 主从采用异步复制,意味着当主节点挂掉时,从节点可能没有收到全部的同步消息,这部分未同步的消息就丢失了。如果主从延迟特别大,那么丢失的数据就可能会特别多。Sentinel 无法保证消息完全不丢失,但是也尽可能保证消息少丢失。它有两个选项可以限制主从延迟过大。

                        min-slaves-to-write 1min-slaves-max-lag 10

                        5、主从模式不足

                        主从模式并不完美,它也存在许多不足之处,下面做了简单地总结:

                        1. Redis 主从模式不具备自动容错和恢复功能,如果主节点宕机,Redis 集群将无法工作,此时需要人为干预,将从节点提升为主节点。

                        2. 如果主机宕机前有一部分数据未能及时同步到从机,即使切换主机后也会造成数据不一致的问题,从而降低了系统的可用性。

                        3. 因为只有一个主节点,所以其写入能力和存储能力都受到一定程度地限制。

                        4. 在进行数据全量同步时,若同步的数据量较大可能会造卡顿的现象。

                        下面来介绍另外一种方式:哨兵模式,主挂掉之后,自动从slave中选举一个作为主机,自动实现故障转移。

                          版权声明:本文内容始发于CSDN>作者:怪 咖@,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。始发链接:https://blog.csdn.net/weixin_43888891/article/details/131039418在此特别鸣谢原作者的创作。此篇文章的所有版权归原作者所有,商业转载建议请联系原作者,非商业转载请注明出处.