Redis 主从复制详细解读

单台 redis 面临的问题

在实际场景中,单一节点的 redis 容易面临风险node

  • 机器故障git

    • 咱们部署到一台 redis 服务器,当发生机器故障时,须要迁移到另外一台服务器而且要保证数据是同步的,而数据是最重要的。单台机器没法保证数据的安全
  • 容量瓶劲github

    • 当咱们有需求须要扩容 redis 内存时,从16G 内存升级到 64G,单机确定是知足不了,除非从新买一个128G的机器
  • 总结redis

    • 要实现分布式数据库更大的存储容量和承受高并发访问量,咱们会将原来集中式数据库的数据分别存储到其余多个网络节点上
    • Redis 为了解决这个单一节点的问题,也会把数据复制多个副本部署到其余节点上进行复制,实现 Redis 的高可用,实现对数据的冗余备份,从而保证数据和服务的高可用

什么是主从复制

  • 主从复制,是指将一台 Redis 服务器的数据,复制到其余的 Redis 服务器,前者成为 master 主节点,后者成为 slave 从节点,数据的复制是单向的,只能由主节点到从节点。
  • 默认状况下,每台 redis 服务器都是主节点,且一个主节点能够有多个从节点,但一个从节点只能有一个主节点

主从复制的做用

数据冗余

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

故障恢复

  • 当主节点出现问题时,能够由从节点提供服务,实现快速的故障恢复,其实是一种服务的冗余

负载均衡

  • 在主从复制的基础上,配合读写分离,能够由主节点提供写服务,由从节点提供读服务。「即写 Redis 数据时应用链接主节点,读 Redis 数据时应用链接从节点」分担服务器负载,尤为是在 「写少读多」 的场景下,经过多个从节点分担读负载,能够大大提升 Redis 服务器的并发量

读写分离

  • 能够用于实现读写分离,主库写,从库读,读写分离不只能够提升服务器的负载能力,同时可根据需求的变化,改变从库的数量

高可用的基石

  • 除了上述的做用外,主从复制仍是哨兵和集群可以实施的基础,所以说主从复制是 Redis 高可用的基石

主从复制启用

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

  • 配置文件数据库

    • 在从服务器的配置文件中加入: slaveof
  • 启动命令安全

    • redis-server 启动命令后加上 --slaveof
  • 客户端命令服务器

    • Redis 服务器启动后,直接经过客户端执行命令 slaveof 则该Redis实例成为从节点
查看复制信息

-   经过 info replication 命令能够看到复制的一些信息

配置文件中注意事项

-   查看 LOG 文件位置
-   保护模式
-   密码配置

主从复制原理

  • 主从复制大致分为3个步骤:

    1.链接创建阶段「准备阶段」网络

    2.数据同步阶段架构

    3.命令传播阶段并发

  • 从节点执行 slaveof 命令后,会进行如下步骤进行复制

    • 主从配置信息以后的日志记录也能够看出以上流程
    1. 保存主节点信息

      • 执行 slaveof 后,redis 会打印如下日志信息\

    2. 主从节点创建 socket 链接

      • 从节点经过内部运行定时任务,维护复制相关逻辑,当定时任务发现新的主节点后,会尝试与该节点进行网络链接

        • 从节点与主节点创建网络链接\

        • 从节点会创建一个 socket 套接字,而后创建一个端口为 51234 的套接字,专门用户接收主节点发送的复制命令,从节点创建链接后的日志\

        • 若是从节点没法创建链接,定时任务会无限重试直到链接成功或者执行 slaveof no one 取消复制
        • 关于链接失败

          • 在从节点执行 info replication 命令查看 master_link_down_since_seconds 这个指标,它会记录与主节点链接失败的系统时间
          • 从节点链接主节点失败时,也会在日志中每秒打印失败信息 # Error condition on socket for SYNC: {socket_error_reason}
    3. 发送Ping 命令

      • 创建链接成功后,从节点发送 Ping 请求进行首次通讯,Ping 命令的主要目的

        • 检测主从之间网络套接字是否可用
        • 检测主节点当前是否能够接收命令
      • 若是发送 Ping 命令以后,从节点没有收到主节点的 Pong 回复或者网络超时,好比网络超时或者主节点阻塞没法响应命令,从节点会断开复制链接,下次定时任务会发起重连
![](https://raw.githubusercontent.com/xiaoshuaizhou/pic/master/pic/轮训重试.png)

    \

    ![](https://raw.githubusercontent.com/xiaoshuaizhou/pic/master/pic/发起重连.png)

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

    ![](https://raw.githubusercontent.com/xiaoshuaizhou/pic/master/pic/ping成功日志.png)

4.  权限验证

    -   若是主节点设置了 requirepass 参数,则须要密码验证,从节点必须配置 masterauth 参数保证与主节点相同的密码才能验证经过,若是验证失败,复制将终止,从节点从新发起复制流程
5.  同步数据集

    -   主从复制链接正常通讯后,对于首次创建的复制场景,主节点会把全部的数据所有发送给从节点,这步是耗时最长的步骤
6.  命令持续复制

    -   当主节点把当前数据所有发送到从节点后,便完成了复制流程,接下来主节点会持续的把命令发送给从节点,保证主从数据的一致性

    ![](https://raw.githubusercontent.com/xiaoshuaizhou/pic/master/pic/命令持续复制.png)

全量复制和部分复制

全量复制

  • 用于初次复制或者其余没法进行部分复制的状况,将主节点的全部数据都发送给从节点,是一个很是重型的操做,当数据量较大时,会对主从节点和网络形成很大的开销
  • 全量复制过程

    1. redis-slave 内部会发出一个同步命令,刚开始是 Psync 命令 , Psync?-1 表示请求 master 主节点同步数据
    2. 主机向从节点发送 runid 和 offset ,由于从节点并无 对应 的 offset ,因此是全量复制
    3. 从节点会保存主机的相关信息 save masterinfo
    4. 主机接收到复制命令后,执行 bgsave「异步执行」在后台生成 RDB 快照文件,并使用一个缓冲区「复制积压缓冲区」来记录如今开始执行的全部写命令
    5. 主机 send RDB 发送 RDB 快照文件给从机
    6. 发送缓冲区数据
    7. 刷新旧的数据,从节点在接收主节点数据以前将以前原有的老数据清空
    8. 加载 RDB 快照文件,将数据库状态更新直主机执行 gbsave 时的数据库状态和缓冲区数据的加载
  • 全量复制的开销

    • bgsave 的时间
    • RDB 文件网络传输时间
    • 从节点清空数据的时间
    • 从节点加载 RDB 文件的时间

部分复制

  • 部分复制是 Redis 2.8 之后出现的,之因此要加上部分复制,是由于全量复制会出现不少的问题,好比开销时间大,没法隔离等问题。\
    redis 但愿在网络抖动的时候,能够有一些机制将复制的损失降到最低;
  • 用于处理在主从复制中因网络闪断等缘由形成的数据丢失场景,当从节点再次连上主节点后,若是条件容许,主节点会补发丢失数据给从节点,由于补发的数据远远小于全量数据,能够有效避免全量复制的太高开销,须要注意的是,若是网络中断时间过长,形成主节点没有可以完整的保存中断期间执行的写命令,则没法进行部分复制,仍使用全量复制
  • 部分复制的过程

    1. 若是网络抖动(connection lost)
    2. Redis 主机仍是会写 replbackbuffer「复制积压缓冲区」
    3. 从机 slave 会继续链接从机
    4. 从机把本身当前的 runid 和 offset 发送给主机,而且执行 Pysnc 执行同步
    5. 若是 master 主机发现偏移量在复制积压缓冲区的范围内,就会返回 continue 命令,继续执行同步
    6. 同步了 offset 的数据,因此部分复制是基于 offset 「偏移量」的

redis 是如何决定使用全量复制或部分复制的?

  • redis 选择决策

    • 从节点将 offset 发送给主节点后,主节点根据偏移量和复制缓冲区大小来决定是否执行部分复制
    • 若是offset 偏移量以后的数据,仍然在复制积压缓冲区的话,使用部分复制
    • 若是 offset 偏移量以后的数据,已经不在积压缓冲区的话,说明数据已经被挤出,因此进行全量复制
  • 如何避免全量复制?

    1. 调整积压缓冲区的大小

      • 因为缓冲区大小固定且有限,所以能够备份的数据也有限,当主从节点的 offset 差距过大超过积压缓冲区时,没法进行部分复制,只能使用全量复制;
      • 反过来讲,为了提升网络中断时部分复制的使用几率,能够根据须要增大复制缓冲区的大小来调节「经过配置 repl_backlog_size」来设置。
      • 例如:若是网络中断的平均时间是 60S,而主节点平均每秒执行的写命令是 100KB,则复制积压缓冲区的平均需求为 6MB, 为了保险期间,设置成 12MB来保证绝大多数断线状况下可使用部分复制
    2. 服务器运行ID「runid」

      • 每一个redis 机器「不管主从」在启动时都会随机生成一个随机的ID「每次启动都不同」,由40个随机的十六进制的字符组成;runid 用来惟一表示 redis 节点,经过 info server 命令查看 runid
      • 主节点初次复制时,主节点将本身的runid 发送给从节点,从节点把这个 runid 保存起来,当断线重连时,从节点将这个 runid 发送给主节点,主节点用这个 runid 来判断是否能够进行部分复制
      • 若是从节点发送的runid 和 主节点的runid 相同,说明主从节点以前通不过,主节点会尝试继续使用部分复制「真正决定使用部分复制的仍是offset 和复制积压缓冲区的大小」
      • 若是从节点发送「保存」的 runid 和如今主节点的 runid 不一样,说明以前主从没有同步过数据,只能进行全量复制

复制偏移量

  • 参与复制中的从节点都会维护自身复制偏移量。
  • 主节点

    • 主节点在处理完写入命令后,会把命令的字节长度作累加记录,统计信息在 info replication 中的 master_repl_offset 指标中
  • 从节点

    • 从节点每秒钟上报自身的复制偏移量给主节点,所以主节点也会保存从节点的复制偏移量
    • 从节点在接收到主节点发送的命令后,也会累加记录自身的偏移量。统计信息在 info replication 中的 slave_repl_offset 中

复制积压缓冲区

  • 复制积压缓冲区:是保存在主节点上的一个固定长度的队列,默认大小是 1MB,当主节点有链接的从节点时被建立,这时主节点响应写命令时,不但会把命令发送给从节点,还会写入复制积压缓冲区
  • 命令传播阶段,主节点除了将写命令发送给从节点,还会发送一份给复制积压缓冲区,做为写命令的备份;
  • 除了存储写命令,复制积压缓冲区中还存储了其中的每一个字节对应的复制偏移量「offset」。
  • 因为复制积压缓冲区定长,而且先进先出。因此它保存的是主节点最近执行的写命令,时间较早的写命令会被挤出缓冲区

主从复制的经常使用相关配置

从数据库配置

  • 1. slaveof

    slave 节点须要配置该项,指向 master 的 IP 和 端口

  • 2.masterauth

    若是 master 启用了密码保护,则配置该项须要填写 master 的启动密码,若是master 未启动该项,则该项须要注释

  • 3. slave-server-stale-data

    指定 master 和 slave 链接中断时的动做。\
    默认是 yes,表示 slave 会继续应答来自 client的请求,但这些数据可能已通过期「由于链接中断可能没法进行master同步」;

    若配置为 no , 则 slave 除正常应答 INFO 和 slaveof 「配置命令」外,其他来自客户端的请求命令均会获得 “SYNC with master in progress” 的应答,直到该 slave 和master重建链接成功或者 slave 提高为 master

  • 4. slave-read-only

    指定 slave 是否为只读,默认为 yes\
    若配置为 no ,表示slave 是可写的,但写的内容在主从同步之后会被清空

  • 5. repl-disable-tcp-nodaly

    指定向 slave 同步数据时,是否禁用 socket 的NO_DALY 选项,若配置为 yes ,则表示禁用 NO_DALY ,则 tcp 协议栈会合并小包统一发送,这样能够减小主从传输间的包数量和减小带宽,但会增长同步到 slave 的时间;

    若配置为 no , 代表启用 NO_DALY,则 tcp 协议栈 不会延迟小包的发送时机,这样数据同步的延时会减小,但须要更大的宽带;

    一般状况下,应该配置为 no 以下降同步的延时,但在主从节点间网络负载很高的状况下,能够配置为 yes

  • 6. slave-priority

    指定 slave 的优先级,在不仅一个 slave 节点的部署环境下,当 master 宕机时,redis-sentinel 会将 priorty 值最小的 slave 提高为 master 。\
    须要注意的是,若是该配置项值为 0,则对应的 slave 永远不会提高为 master

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

读写分离

  • 流量分摊到从节点,这是个很是好的特性,若是一个业务只须要读数据,那么能够创建一台从机进行读数据操做
  • 虽然读写有优点,可以让读这部分分配给各个从节点,若是不够直接添加 slave 从节点,可是会出现如下问题

    • 复制数据延迟

      • 可能出现 从节点同步数据不及时,致使数据不一致的问题,固然可使用监控偏移量,若是 offset 超出偏移量,就切换到 master 机器上,逻辑切换,具体延迟多少可使用 info replication 命令查看 offset 指标进行排查
      • 对于没法容忍大量延迟的场景,能够编写外部监控程序,「如consul」监听主从节点的复制偏移量,当延时较大时出发报警,或通知客户端,避免读取延时太高的节点
    • 对于N个从节点链接的时候才容许写入「比较极端的方式」

      • Redis2.8 之后,能够设置主节点只有在N台从节点链接的时候能够写入请求。\
        然而,由于 redis 使用的是异步复制,全部没有办法保证从节点确实收到的给定的写入请求,因此存在一个窗口期的数据丢失的可能性。
      • 解释如下这个特性是怎么工做的

        • 从节点每秒都会 Ping 主节点,告知它全部的复制流工做。主节点会记住从每一个从节点收到的最新的 Ping
        • 用户能够给主节点配置一个在等于从节点的数量的最低值和不超过最高值之间的延迟,若是至少有N个从节点,若是少于延迟M秒,则写入将被接受。
        • 可能以为经最大努力保证数据安全的机制,虽然数据一致性没法保证,可是至少只是几面的时间窗口内丢失数据,一般状况下范围数据丢失可比无范围数据丢失好多了
        • 若是条件不知足,主节点将会返回一个错误,而且写入请求将不被接受
        • 主节点配置须要两个参数

          • min-slaves-to-write
          • min-slaves-max-lag
    • 从节点故障问题

      • 对于从节点的故障问题,须要在客户端维护一个可用从节点列表,当从节点故障时,当即切换到其余从节点或主节点

主从配置不一致

  • 常常致使主机和从机的配置不一样,并带来问题
  • 主机和从机有时候会发生配置不一致的状况,

    例如:maxmemory 不一致,若是主机配置 maxmemory 为8G,从机 slave 设置为 4G,这个时候是能够用的,还不会报错,单数若是要作高可用,让从节点变成主节点的时候,就会发现数据已经丢失了,并且没法挽回

规避全量复制

  • 全量复制指的是,当 slave 从机断掉并重启后,runid 产生变化而致使须要在 master 主机里拷贝所有数据,这种拷贝所有数据的过程很是耗资源
  • 全量复制是不可避免的。

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

  • 形成全量复制的缘由

    1. 主从机的运行 runid 不匹配。
主节点若是重启,runid 将会发生变化,若是从节点监控到 runid 是不一样一个,它就会认为你的节点不安全,当发生故障转移的时候,若是主节点发生故障,那么从机就会变成主节点。

1.  复制缓冲区空间不足。

好比默认值:1M 能够部分复制,但若是缓冲区不够大的话,首先须要网络中断,部分复制就没法知足,其次须要增大复制缓冲区配置「relbacklogsize」,对网络的缓冲加强

解决方案:

-   在一些场景下,可能但愿对主节点进行重启。

例如:主节点内存碎片率太高,或者但愿调整一些只能在启动时调整的参数,若是使用普通的手段重启主节点,会使得 runid 发生变化,可能致使没必要要的全量复制

-   为了解决这个问题,redis 提供了 debug reload 的重启的方式,重启后,主节点的 runid 和 offset 都不受影响,避免了 全量复制

3.当一个主机下面挂了不少个 slave 从机的时候,主机master 挂了,这时 master 主机重启后,由于 runid 发生了变化,全部的 slave 从机都要作一次全量复制,这将引发但节点和但机器的复制风暴,开销会很是大

-   解决方案:

    -   通常使用一主多从,由于主节点还同时担任写操做
    -   能够采用树状结构下降多个从节点对主节点的消耗
    -   从节点采用树状结构很是有用,网络开销给位于中间层的从节点,而没必要消耗顶层的主节点,可是这种树状结构也带来了运维的复杂性,增长了手动和自动处理故障转移的难度\

        ![](https://raw.githubusercontent.com/xiaoshuaizhou/pic/master/pic/树状结构.png)

规避复制风暴

  • 解决方案:

    • 因为 redis 的单线程架构,一般单台机器会部署多个 redis 实例,当一台机器上同时部署多个主节点「master」时,若是每一个 master 主机只有一台 slave 从机,那么当机器宕机之后,会产生大量的全量复制,这种状况是很是危险的,带宽立刻被占用,会致使不可用
    • 应该把主节点尽可能分散在多台机器上,避免在单台机器上部署过多的主节点。当主节点所在的机器故障后提供故障转移机制,避免机器恢复后进行密集的全量复制

如何选择,要不要读写分离

没有最合适的方案,只有最合适的场景,读写分离须要业务能够容忍必定程度的数据不一致,适合读多写少的业务场景,读写分离,是为了要创建一主多从的架构,才能横向任意扩展 slave node 去支撑更大的读吞吐量

相关文章
相关标签/搜索