Mr Dk.'s BlogMr Dk.'s Blog
  • 🦆 About Me
  • ⛏️ Technology Stack
  • 🔗 Links
  • 🗒️ About Blog
  • Algorithm
  • C++
  • Compiler
  • Cryptography
  • DevOps
  • Docker
  • Git
  • Java
  • Linux
  • MS Office
  • MySQL
  • Network
  • Operating System
  • Performance
  • PostgreSQL
  • Productivity
  • Solidity
  • Vue.js
  • Web
  • Wireless
  • 🐧 How Linux Works (notes)
  • 🐧 Linux Kernel Comments (notes)
  • 🐧 Linux Kernel Development (notes)
  • 🐤 μc/OS-II Source Code (notes)
  • ☕ Understanding the JVM (notes)
  • ⛸️ Redis Implementation (notes)
  • 🗜️ Understanding Nginx (notes)
  • ⚙️ Netty in Action (notes)
  • ☁️ Spring Microservices (notes)
  • ⚒️ The Annotated STL Sources (notes)
  • ☕ Java Development Kit 8
GitHub
  • 🦆 About Me
  • ⛏️ Technology Stack
  • 🔗 Links
  • 🗒️ About Blog
  • Algorithm
  • C++
  • Compiler
  • Cryptography
  • DevOps
  • Docker
  • Git
  • Java
  • Linux
  • MS Office
  • MySQL
  • Network
  • Operating System
  • Performance
  • PostgreSQL
  • Productivity
  • Solidity
  • Vue.js
  • Web
  • Wireless
  • 🐧 How Linux Works (notes)
  • 🐧 Linux Kernel Comments (notes)
  • 🐧 Linux Kernel Development (notes)
  • 🐤 μc/OS-II Source Code (notes)
  • ☕ Understanding the JVM (notes)
  • ⛸️ Redis Implementation (notes)
  • 🗜️ Understanding Nginx (notes)
  • ⚙️ Netty in Action (notes)
  • ☁️ Spring Microservices (notes)
  • ⚒️ The Annotated STL Sources (notes)
  • ☕ Java Development Kit 8
GitHub
  • ⛸️ Redis Implementation
    • Part 1 - 数据结构与对象

      • Chapter 2 - 简单动态字符串
      • Chapter 3 - 链表
      • Chapter 4 - 字典
      • Chapter 5 - 跳跃表
      • Chapter 6 - 整数集合
      • Chapter 7 - 压缩列表
      • Chapter 8 - 对象
    • Part 2 - 单机数据库的实现

      • Chapter 9 - 数据库
      • Chapter 10 - RDB 持久化
      • Chapter 11 - AOF 持久化
      • Chapter 12 - 事件
      • Chapter 13 - 客户端
      • Chapter 14 - 服务器
    • Part 3 - 多机数据库的实现

      • Chapter 15 - 复制
      • Chapter 16 - Sentinel
      • Chapter 17 - 集群
    • Part 4 - 独立功能的实现

      • Chapter 18 - 发布与订阅
      • Chapter 19 - 事务
      • Chapter 21 - 排序
      • Chapter 22 - 二进制位数组
      • Chapter 23 - 慢查询日志

Chapter 15 - 复制

Created by : Mr Dk.

2020 / 06 / 09 16:28

Nanjing, Jiangsu, China


Redis 可以通过执行 SLAVEOF 命令或设置选项,去复制另一个服务器。被复制的服务器称为 主服务器 (master),对主服务器进行复制的服务器称为 从服务器 (slave)。

Old Implementation

在 Redis 2.8 之前,使用的是一种旧版本的复制思想,主要分为两步:

  • 同步 (sync) - 将从服务器的数据库状态更新到主服务器的状态
  • 命令传播 (command propagate) - 在主服务器上运行的命令也要在从服务器上运行一遍

旧版复制方案的问题:本质上相当于 RDB 与 AOF 的差异,可以理解为整体的同步和增量式的同步。整体同步必然会带来较大的开销,新版 Redis 的增量式复制可以有效减小开销。

同步

从服务器向主服务器发送 SYNC 命令,主服务器执行 BGSAVE 命令,在后台生成一个 RDB 文件并发送给从服务器,同时在一个缓冲区中记录从现在开始主服务器执行的所有写命令。从服务器载入 RDB 文件后,执行主服务器中缓存的所有写命令,将数据库状态更新为与主服务器一致。

  • RDB 写出与载入
  • 写命令缓冲区

命令传播

每当 主服务器 执行客户端发送的写命令时,主服务器会将命令发送给从服务器执行,以保证两个 Redis 服务器的数据一致。

旧版复制的缺陷

从服务器对主服务器的复制分为两种情况:

  • 初次复制 - 从服务器从未复制过主服务器,或要复制的主服务器与上次复制的主服务器不同
  • 断线后重复制 - 主从服务器在命令传播阶段因网络原因中断复制,但之后服务器自动重连

对于后一种情况,旧版复制显得很累赘,效率很低。为了让服务器补足一小部分缺失的数据,而让主从服务器重新执行一次 SYNC 命令,开销过大。

  • SYNC 过程中,主服务器生成 RDB 文件需要消耗大量的 CPU、内存、I/O 资源
  • RDB 文件在网络上的传输会占据较大带宽、流量
  • 从服务器在载入 RDB 文件期间会因阻塞无法响应请求

New Implementation

Redis 2.8 开始使用 PSYNC 代替 SYNC 执行同步操作。该命令具有 完整重同步 和 部分重同步 两种模式:

  • 完整重同步与原 SYNC 命令基本一致,通过 RDB + 写命令缓冲区进行同步
  • 部分重同步处理断线后重复制的情况,只执行连接断开期间主服务器上的写命令

部分重同步的实现

Redis 的主服务器维护了一个 复制积压缓冲区,是一个默认大小为 1MB 的 RingBuffer。主服务器进行命令传播时,不仅会将写命令发送给所有从服务器,还会写入复制积压缓冲区中,从而保存最近 1MB 的写命令。另外,复制积压缓冲区中的每个字节都有其 复制偏移量。

当从服务器连上主服务器时,会将自己的复制偏移量 offset 发送给主服务器。如果 offset 落后于主服务器,且 offset + 1 开始的数据存在于复制积压缓冲区中,则主服务器直接对从服务器进行部分重同步操作;如果 offset + 1 开始的数据已经不存在于复制积压缓冲区中,则主服务器对从服务器执行完整重同步。

复制积压缓冲区的大小可以自行设置。最小大小 = 主服务器每秒写入命令数据量 _ 从服务器重连主服务器的平均时间。为安全起见,可以将最小大小 _ 2。

服务器运行 ID

Redis 服务器在启动时自行生成一个长度为 40 的十六进制字符串。当从服务器第一次复制主服务器时,会保存主服务器的运行 ID。当断线重连后,从服务器向当前主服务器发送运行 ID。如果主服务器发现收到的 ID 和自身 ID 相同,说明这是一次断线重连,可以尝试部分重同步;否则,说明从服务器之前复制的主服务器不是自己,需要进行完整重同步。


心跳检测

从服务器默认以每秒一次的频率,向主服务器发送命令,其中带有从服务器目前的 复制偏移量。命令有三个作用:

  • 检测主从服务器的网络连接状态 - 命令是否收到
  • 辅助实现 min-slaves 选项 (从服务器数量较少/延迟较高时,主服务器禁止执行写命令)
  • 检测命令丢失 - 根据复制偏移量,确认命令传播是否有不成功的情况
Edit this page on GitHub
Next
Chapter 16 - Sentinel