学习总结录 学习总结录
首页
归档
分类
标签
  • Java基础
  • Java集合
  • MySQL
  • Redis
  • JVM
  • 多线程
  • 计算机网络
  • 操作系统
  • Spring
  • Kafka
  • Elasticsearch
  • Python
  • 面试专题
  • 案例实践
  • 工具使用
  • 项目搭建
  • 服务治理
  • ORM框架
  • 分布式组件
  • MiniSpring
  • 设计模式
  • 算法思想
  • 编码规范
友链
关于
GitHub (opens new window)
首页
归档
分类
标签
  • Java基础
  • Java集合
  • MySQL
  • Redis
  • JVM
  • 多线程
  • 计算机网络
  • 操作系统
  • Spring
  • Kafka
  • Elasticsearch
  • Python
  • 面试专题
  • 案例实践
  • 工具使用
  • 项目搭建
  • 服务治理
  • ORM框架
  • 分布式组件
  • MiniSpring
  • 设计模式
  • 算法思想
  • 编码规范
友链
关于
GitHub (opens new window)
  • Java基础

  • Java集合

  • MySQL

  • Redis

    • Redis核心技术01-基础数据结构
    • Redis核心技术02-线程IO模型
    • Redis核心技术03-持久化
    • Redis核心技术04-数据同步
      • Redis核心技术04-数据同步
      • 主从库模式
      • 数据同步
        • 如何同步
        • 主从级联模式
        • 主从网络断连
      • 参考
    • Redis核心技术05-哨兵机制
    • Redis核心技术06-哨兵集群
    • Redis核心技术07-切片集群
    • Redis核心技术08-String
    • Redis核心技术09-keys统计案例与方案
    • Redis核心技术10-GEO
    • Redis核心技术11-时间序列数据存储
    • Redis核心技术12-消息队列
    • Redis核心技术13-异步机制
    • Redis核心技术14-CPU结构对Redis性能影响
    • Redis核心技术15-应对变慢的Redis
    • Redis核心技术16-删除数据后内存占用率还是很高
    • Redis核心技术17-缓冲区
    • Redis核心技术18-Redis缓冲是如何工作的
    • Redis核心技术19-缓冲替换策略
    • Redis核心技术20-缓冲异常
    • Redis核心技术21-缓存污染
    • Redis核心技术22-无锁原子操作
    • Redis核心技术23-分布式锁
    • Redis核心技术24-事务机制
    • Redis核心技术25-主从同步与故障切换的坑
    • Redis核心技术26-脑裂问题
    • Redis核心技术27-Redis在秒杀场景的关键技术
  • JVM

  • 多线程

  • 计算机网络

  • Spring

  • Kafka

  • Elasticsearch

  • Python

  • 面试专题

  • 知识库
  • Redis
旭日
2023-03-31
目录

Redis核心技术04-数据同步

# Redis核心技术04-数据同步

Redis在AOF和RDB的帮助下,即使发生宕机,也可以分别通过回放日志和重新读入RDB文件的方式来恢复数据,从而保证数据的可靠性。

但是由于在恢复的过程中比较缓慢,如果我们只有一台Redis实例,意味着在恢复期间,是无法服务新来的数据存取请求的。所以在AOF和RDB保证数据尽量少丢失下,我们还需要使用主从库来保证服务尽量少中断,以此来达到高可靠性。

# 主从库模式

实际上,Redis 提供了主从库模式,以保证数据副本的一致,主从库之间采用的是读写分离的方式。

  • 读操作:主库、从库都可以执行。
  • 写操作:首先到主库执行,然后,主库将写操作同步给从库。

image-20220531103843421

为何采用读写分离的方式呢

试想一个问题,如果服务器上有三个Redis的实例(一个主库,两个从库),客户端发起写请求,对某一个key进行了三次修改,而这三次修改又是在不同实例上进行,那么这个数据在三个实例上的副本就不一致了。以后在读取这个数据的时候,就可能读取到旧的数值。

如果在上诉情况下要强行保证这个数据在三个实例上一致,就要涉及到加锁的问题,这样就会带来额外的开销,对于快速的Redis是不能接受的。

而主从库模式一旦采用了读写分离,所有数据的修改只会在主库上进行,不用协调三个实例。主库有了最新的数据后,会同步给从库,这样,主从库的数据就是一致的。

# 数据同步

对于数据同步我们要思考两个问题:

  • 同步的过程是这么样的?是一次性同步,还是分批同步。
  • 如果同步过程中,网络发生故障,数据还能保持一致吗?

# 如何同步

当我们启动多个 Redis 实例的时候,它们相互之间就可以通过 replicaof(Redis 5.0 之前 使用 slaveof)命令形成主库和从库的关系,之后会按照三个阶段完成数据的第一次同步。

image-20220531110145407

第一阶段

第一阶段是主从库建立连接、协商同步的过程,主要是为第二阶段的全量复制做准备。

从库给主库发送psync命令,表示要进行数据同步,主库根据这个命令的参数来启动复制。psync命令包含了主库的 runID 和复制进度 offset 两个参数。

  • runID:是每个 Redis 实例启动时都会自动生成的一个随机 ID,用来唯一标记这个实例。当从库和主库第一次复制时,因为不知道主库的 runID,所以将 runID 设为?。
  • offset:此时设为 -1,表示第一次复制。

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

FULLRESYNC 响应表示第一次复制采用的全量复制,也就是说, 主库会把当前所有的数据都复制给从库

第二阶段

主库将所有数据同步给从库,从库收到数据后,在本地完成数据加载。

主库执行bgsave命令,生成RDB文件,然后主库将RDB文件同步给从库。从库接受到文件后,先清空现有数据,然后加载RDB文件。

为什么要清除数据

这是因为从库在和主库同步之前,可能保持了其他的数据,为了避免之前数据的影响,从库需要把当前数据库清空。

第三阶段

在主库将数据同步给从库的过程中,主库不会阻塞,仍然可以接收请求。但是由于传递给从库的是RDB文件,相当于是同步那一刻的快照,那么新请求的写操作并没有记录到刚刚生成的RDB文件中。为了保证主从库的数据一致性主库会在内存中用专门的 replication buffer,记录在 RDB 文件生成后收到的所有写操作。

在第三阶段主库会把第二阶段执行过程中新接收的写命令,在发送给从库。具体的操作是,当主库完成 RDB 文件发送后,就会把此时 replication buffer 中的修改操作发给从库,从库再重新执行这些操作。这样一来,主从库就实现同步了。

# 主从级联模式

在主从第一次数据同步过程中要进行一次全量复制,这对于主库来说需要完成两个耗时的操作:生成RDB文件和创建RDB文件。

  • 生成RDB文件是需要fork子进程的,由于fork这个操作会阻塞主线程处理正常请求,从而导致主库响应应用程序的请求速度变慢。

  • 传输RDB文件是需要占用主库的网络带宽的,这也会给主库的资源使用带来压力。

为了分担主库的压力,就需要引入主-从-从模式:

主从库模式中,所有的从库都是和主库连接,所有的全量复制也都是和主库进行的。现在,我们可以通过主 - 从 - 从模式将主库生成 RDB 和传输 RDB 的压力, 以级联的方式分散到从库上。

image-20220531143308453

通过这个方式,某一些从库在同步的时候,就不用再和主库进行交互了,只要和级联的从库进行写操作同步就行了,这样也减轻主库上的压力。

主从库完成了全量复制,它们之间就会一直维护一个网络连接,主库会通过这个连接将后续陆续收到的命令操作再同步给从库,这个过程也称为基于长连接的命令传播,可以避免频繁建立连接的开销。

# 主从网络断连

但是在这个连接中存在一个隐患,最常见的就是网络断连或阻塞。如果网络断连,主从库之间就无法进行命令传播了,从库的数据自然也就没办法和主库保持一致了,客户端就可能从从库读到旧数据。

在主从网络断连后,主从库会采用增量复制的方式继续同步。

  • 全量复制:同步所有数据。
  • 增量复制:只会把主从库网络断连期间主库收到的命令,同步给从库。

增量复制的时候,主从库之间是通过缓冲区来保持同步的。

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

repl_backlog_buffer 是一个环形缓冲区,主库会记录自己写到的位置,从库则会记录自己 已经读到的位置。

image-20220531151430884

刚开始的时候,主从库写读的位置相同。随着主库不断接受新的写操作,它的写位置就会逐步偏离起始位置。我们通常用偏移量来衡量这个偏移距离的大小,对主库来说,对应的偏移量就是 master_repl_offset。主库接收的新写操作越多,这个值就会越大。

同理,从库在复制写操作命令之后,从库的读位置也会发生偏移,从库的偏移量 slave_repl_offset 也在不断增加。正常情况下,这两个偏移量基本相等。但是在主从断连的时候,由于主库还在不断接受请的请求,因此它的偏移量仍然在增大,而从库无法进行读,所以偏移量不会动,这就会导致master_repl_offset 会大于 slave_repl_offset。

在主从库的连接恢复之后,从库首先会给主库发送 psync 命令,并把自己当前的 slave_repl_offset 发给主库,主库会判断自己的 master_repl_offset 和 slave_repl_offset 之间的差距,只用把 master_repl_offset 和 slave_repl_offset 之间的命令操作同步给从库就行。

image-20220531152141530

由于repl_backlog_buffer 是一个环形缓冲区,所以在缓冲区写满后,主库会继续写,此时就会覆盖掉之前写入的操作。如果从库读取速度过慢,就可能导致从库还没有读取的数据就被主库新写的数据覆盖掉,这会导致主从库的数据不一致。

为了避免这种情况,就需要调整repl_backlog_size这个参数。缓冲空间的计算公式是:缓冲空间大小 = 主库写入命令速度 * 操作大小 - 主从库间网络传输命令速度 * 操作大小。在实际应用中,考虑到可能存在一些突发的请求压力,我们通常需要把这个缓冲空间扩大一倍,即 repl_backlog_size = 缓冲空间大小 * 2,这也就是 repl_backlog_size 的最终值。

# 参考

Redis核心技术与实战 (opens new window)

#Redis
上次更新: 2024/06/29, 15:13:44
Redis核心技术03-持久化
Redis核心技术05-哨兵机制

← Redis核心技术03-持久化 Redis核心技术05-哨兵机制→

最近更新
01
基础概念
10-31
02
Pytorch
10-30
03
Numpy
10-30
更多文章>
Theme by Vdoing | Copyright © 2021-2024 旭日 | 蜀ICP备2021000788号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式