• Redis-浅谈主从同步


    主从库集群

    Redis 提供了主从库模式,以保证数据副本的一致,在从库执行一下命令可以建立主从库关系:

    replicaof  
    

    Redis 的主从库之间采用的是读写分离的方式:

    1. 读操作:主库、从库都可以接收;
    2. 写操作:到主库执行,然后将写操作同步给从库。

    写操作只在主库执行,主要是为了避免多实例写导致的数据一致性问题,减少多实例之间数据一致的协商开销。

    主从同步是如何进行的

    下图是主从第一次同步的流程:

    https://raw.githubusercontent.com/LooJee/medias/master/images202208041128834.png

    第一阶段

    第一阶段,从库会给主库发送 psync 命令,类似 tcp 的握手,该命令有两个参数:主库的 runid 和复制进度 offset。

    • runid 是每个 Redis 实例启动时都会自动生成的一个随机 ID,用来标记这个实例。从库第一次和主库同步时,不知道主库的 runid ,所以将 runid 设置为 “?”;
    • offset 是从库到目前为止处理的偏移量,第一次同步的时候会传 -1 。

    收到 psync 命令后,主库会用 FULLRESYNC 响应带上主库 id 和当前的复制进度 offset 返回给从库,从库会记录这两个参数。

    第二阶段

    主库在收到 psync 后,会执行 bgsave 命令,生成 RDB 文件,然后将文件发送给从库。从库收到文件后,会先情况当前的数据,然后加载 RDB 文件。

    第三阶段

    在从库处理完 RDB 文件时,主库会将期间处理的写操作放在 replication buffer 中,等到从库处理完 RDB 文件后,主库会将修改操作都发送给从库执行,从而完成主从同步。

    基于长连接的命令传播

    主从库的连接建立成功,并且完成第一次的全量同步之后,主从库之间会维持一个长链接,主库会将之后接收到的写操作同步给从库。

    增量同步

    在使用过程中,可能会出现主从库之间网络闪断的情况,如果恢复连接后采用全量同步的方式,必然会有很大的开销。Redis 2.8 之后,采用增量同步的方式来完成这个操作。

    当主从断连之后,主库会把期间收到的写操作命令写入 replication buffer,同时也会把这些操作命令也写入 repl_backlog_buffer 这个缓冲区。

    repl_backlog_buffer 是一个环形缓冲区,主库会记录自己的偏移量 master_repl_offset,从库会记录自己的偏移量 slave_repl_offset。用命令 info Replication 可以查看对应的 offset。

    主从库恢复连接后,从库会用 psync 发送自己的 slave_repl_offset 给主库,主库对比自己的 master_repl_offset ,将两个 offset 之间的写操作同步给从库。

    https://raw.githubusercontent.com/LooJee/medias/master/images202208051108463.png

    因为 repl_backlog_buffer 是一个环形队列,所以,如果从库的读取速度比较慢,就有可能导致从库还未读取的操作被主库新写的操作覆盖,如果主库接收从库的 psync 时发现从库的 offset 已经被覆盖,为了不丢失数据那么就会发起全量同步。为了避免全量同步,这时候就需要增加 repl_backlog_size 的值,这个值和缓冲空间大小有关,缓存空间大小 = 主库写入命令速度 * 操作大小 - 主从库间网络传输命令速度 * 操作大小 。考虑到突发压力,通常 repl_backlog_size 会设置为 计算结果的 2 到 4 倍。

    级联主从

    如果一个主库下有很多的从库,这些从库都要和主库进行全量同步的时候,主库的压力会非常大,忙于 fork 子进程生成 RDB 文件,影响主线程处理客户端请求。这是可以将主从结构改成主→ 从 → 从 的联机结构,缓解主库的压力。

    实验

    我们可以用 docker-compose 实验 Redis 主从同步:

    version: "3"
    services:
      redis-master:
        image: redis:7
        ports: 
        - "16379:6379"
        container_name: "redis-master"
        command: redis-server
        networks:
        - redis-replica
      redis-slave-1:
        image: redis:7
        ports:
        - "6380:6379"
        container_name: "redis-slave-1"
        command: redis-server --replicaof redis-master 6379 
        depends_on: 
        - redis-master
        networks:
        - redis-replica
      redis-slave-2:
        image: redis:7
        ports:
        - "6381:6379"
        container_name: "redis-slave-2"
        command: redis-server --replicaof redis-slave-1 6379 
        depends_on: 
        - redis-slave-1
        networks:
        - redis-replica
    networks:
      redis-replica:
    

    这里定义了一个主库 redis-master 以及两个从库 redis-slave-1、redis-slave-2。它们是一个级联的主从关系 redis-master ← redis-slave-1 ← redis-slave-2

    https://raw.githubusercontent.com/LooJee/medias/master/images202208051158232.png

    我预先在 master 中插入了一些数据之后在设置了两个 slave 节点。启动之后,可以看到 slave2 先向 salve1 发起了同步请求,但是 slave1 还没和 master 完成同步,所以 salve2 一直在重试,直到 salve1 和 master 完成同步后才开始 slave2 和 salve1 之间的数据同步。

  • 相关阅读:
    C# 6.0 添加和增强的功能【基础篇】
    Unity中Shader的屏幕坐标
    机器学习入门(三)多元线性回归
    一文搞定Postman(菜鸟必看)
    linux安装edge时出现dpkg依赖问题
    简易数控直流稳压电压
    docker rabbitmq 宕机 无反应 不消费消息
    机器学习强基计划0-4:通俗理解奥卡姆剃刀与没有免费午餐定理
    征服BAT精选《Spring 源码分析》核心,欢迎围观
    Java常用的设计模式
  • 原文地址:https://www.cnblogs.com/cyberbug/p/16554075.html