Redis-持久化、高可用

羊37 / 2024-06-05 / 原文

参考文章:

Redis持久化机制详解

Redis详解(二)——AOF

Redis 高可用:主从复制、哨兵模式、集群模式

1.缓存三兄弟

“缓存三兄弟”通常是指缓存、持久化和高可用性,这是缓存系统的三个核心概念。

1.1 缓存

缓存是将数据暂时存储在高速访问的存储介质(如内存)中,以提高数据访问速度。

Redis 作为一个内存数据库,主要用于缓存数据。

缓存的优点包括提高系统响应速度和减少数据库负载。

1.2 持久化

持久化是指将数据从易失性存储(如内存)保存到非易失性存储(如磁盘)中,以确保数据在系统重启或故障时不会丢失。

1.3 高可用

高可用性是指系统能够在部分组件失效的情况下继续提供服务。

Redis 通过主从复制(Replication)和哨兵(Sentinel)机制来实现高可用性。

主从复制可以在主节点故障时迅速切换到从节点,哨兵机制则负责监控 Redis 实例并自动进行故障转移。

2.Redis持久化

1.1 为什么

Redis是基于内存的,不保存的话,应用停止了后数据就不在了。

持久化的诉求,主要是解决以下问题:

防止数据丢失

Redis 是一个内存数据库,数据主要存储在内存中。如果没有持久化机制,一旦服务器宕机或重启,内存中的所有数据都会丢失。通过持久化,Redis 可以在磁盘上保存数据的副本,在服务器重启后恢复数据,确保数据的完整性和连续性。

数据备份和恢复

持久化机制允许定期生成数据快照,并将其存储在磁盘上。这些快照可以用于备份和灾难恢复。当出现硬件故障、数据损坏或其他不可预见的问题时,可以从备份中恢复数据,减少数据丢失的风险。

数据迁移

通过持久化文件,可以方便地在不同的 Redis 实例之间迁移数据。例如,可以将 RDB 或 AOF 文件从一个服务器复制到另一个服务器,然后在新服务器上加载文件,实现数据的迁移。

高可用性和数据共享

持久化文件可以与其他数据存储系统共享,例如将 Redis 数据与其他数据库系统进行集成,或将数据导出到其他分析工具中进行处理。持久化确保了数据在不同系统间的可用性和一致性。

应对计划外重启

在进行服务器维护、软件升级或其他操作时,可能需要重启 Redis 实例。持久化机制保证了在这些情况下,重启后的数据可以快速恢复,减少系统停机时间,提高系统的可用性和可靠性。

应对计划外重启

持久化的日志文件(例如 AOF 文件)记录了所有的写操作。这些日志文件不仅可以用于数据恢复,还可以用于审计和分析。例如,分析日志文

Redis中的持久化,主要有3种方式。

持久化方式 描述 优点 缺点
RDB(Redis DataBase) 在指定的时间间隔内生成数据集的快照,并将快照保存到磁盘 - 文件紧凑高效,适合备份和灾难恢复
- 加载 RDB 文件速度快
- 可能丢失自上次快照以来的数据变化
- 生成快照时对性能有一定影响
AOF(Append Only File) 记录每个写操作到日志文件,并根据同步策略将日志同步到磁盘 - 数据丢失窗口期短
- 文件格式易于理解和修改
- 文件通常比 RDB 大,恢复时间较长
- 写操作频繁时对性能有影响
混合持久化(RDB + AOF) 结合 RDB 和 AOF 的优点,同时使用两种持久化方式 - 提供完整快照和细粒度日志记录
- 减少数据丢失风险
- 需要更多的存储空间
- 复杂性增加

1.2 RDB(Redis DataBase)(默认)

Redis 的 RDB持久化机制是一种通过定期生成内存数据的快照(snapshot)并保存到磁盘的方法,以确保数据的持久性。

摄影的快照和慢照,强调的是快门的速度。

快照就相当于我们平时拍照片,立马就拍好了,记录的是一个瞬间的状态。

  • Redis的快照文件通常以 dump.rdb 的形式存储在磁盘中。
  • Redis 会通过创建一个子进程来执行这个快照生成过程,以最小化对主进程的影响。

RDB的优点就是快和简单,但是缺点也很明显,因为快照不是时时刻刻都在记录。

不适合那些数据变更频繁、需要实时持久化的应用场景。

1.2.1 配置与命令

RDB 持久化机制默认是基于键的变化触发的。

如果xx秒内有xx个键变化,则触发一次全量快照。

1.2.1.1 命令行

  • SAVE:同步执行快照生成,会阻塞 Redis 服务器,直到快照生成完成。
  • BGSAVE:异步执行快照生成,不会阻塞 Redis 服务器。Redis 会立即返回 OK,然后fork 出一个子进程,快照生成过程在后台进行。

1.2.1.2 配置文件

# 保存快照的条件
# 自动触发bgsave命令创建快照
save 900 1       # 如果900秒内至少有1个key发生变化,则触发快照
save 300 10      # 如果300秒内至少有10个key发生变化,则触发快照
save 60 10000    # 如果60秒内至少有10000个key发生变化,则触发快照

# 指定快照文件的路径和名称
dir /var/lib/redis
dbfilename dump.rdb

redis启动的时候,会自动加载配置文件中的dump文件,重启自动恢复。

# 查看dump文件位置
cat /path/to/redis.conf | grep dir

嗯,既然是通过键触发的,我想搞成定时的咋办?

利用BGSAVE命令,比如cron脚本、应用代码等。

1.2.2 dump.rdb

这是个二进制文件,直接打开是乱码的。

有的字符可见,是因为Vim 默认情况下会尝试以 ASCII 或 UTF-8 格式打开文件。

image-20240604130348956

嗯,具体的语法解析,可以使用python工具:redis-rdb-tools

或者,直接用redis-check-rdb命令,一般安装后就自带了,可以查看到一些dump文件的基本信息。

# redis-check-rdb dump.rdb 

[offset 0] Checking RDB file dump.rdb          # 从文件的第0字节开始检查RDB文件dump.rdb
[offset 26] AUX FIELD redis-ver = '7.0.8'      # 辅助字段,表示生成RDB文件的Redis版本是7.0.8
[offset 40] AUX FIELD redis-bits = '64'        # 辅助字段,表示Redis运行在64位架构上
[offset 52] AUX FIELD ctime = '1717123314'     # 辅助字段,创建时间戳(Unix时间格式) 2024-05-31 02:41:54
[offset 67] AUX FIELD used-mem = '2178568'     # 辅助字段,生成RDB文件时Redis使用的内存量为2178568字节(大约2.08MB)
[offset 79] AUX FIELD aof-base = '0'           # 辅助字段,AOF文件的基准大小为0,可能是因为没有使用AOF持久化或AOF文件尚未生成
[offset 81] Selecting DB ID 0                  # 选择数据库ID 0,开始解析数据库0的数据
[offset 329] Selecting DB ID 1                 # 选择数据库ID 1,开始解析数据库1的数据
[offset 1031] Checksum OK                      # 校验和验证通过,RDB文件没有数据损坏
[offset 1031] \o/ RDB looks OK! \o/            # 成功信息,RDB文件结构和内容看起来都正常,没有检测到问题
[info] 10 keys read                            # 读取到10个键
[info] 0 expires                               # 没有键设置过期时间
[info] 0 already expired                       # 没有键已经过期

1.2.3 frok进程

fork() 系统调用在操作系统中用于创建一个新进程,这个新进程被称为子进程。

fork() 进程实际上是对当前进程的一个复制,但有部分点要注意。

  • 复制进程

    • 当一个进程调用 fork() 时,操作系统会创建一个新进程(子进程),这个新进程是调用进程(父进程)的副本。
    • 子进程会复制父进程的地址空间,包括代码段、数据段、堆和栈。子进程几乎完全与父进程相同,但有几个关键的区别。
  • 独立的进程

    • 子进程与父进程是独立的进程,它们各自拥有自己的进程标识符(PID)。
    • 子进程的 PID 与父进程不同,子进程的父进程 ID(PPID)是父进程的 PID。
  • 返回值

    • fork() 的返回值
      • 在父进程中,fork() 返回子进程的 PID。
      • 在子进程中,fork() 返回 0。
    • 通过检查 fork() 的返回值,程序可以确定当前正在运行的是父进程还是子进程

image-20240604134700820

1.3 AOF(Append Only File)

AOF 是 Redis 提供的另一种持久化机制,通过将每一个写操作追加到日志文件中,确保数据的持久性。

AOF 文件记录了所有对数据库进行的写操作指令,这些指令可以用于在服务器启动时重新执行,从而重建数据库的状态。

与快照持久化相比,AOF 持久化的实时性更好。

默认情况下 Redis 没有开启 AOF(append only file)方式的持久化(Redis 6.0 之后已经默认是开启了)

1.3.1 配置与命令

1.3.1.1 配置文件

# 启用 AOF 持久化
appendonly yes

# AOF 文件名
appendfilename "appendonly.aof"

# 文件同步策略
appendfsync everysec   # 每秒同步一次
# appendfsync always   # 每次写操作都同步
# appendfsync no       # 不主动同步,让操作系统决定何时同步

# 重写期间是否继续 fsync
no-appendfsync-on-rewrite no

# 自动重写配置:创建一个新的 AOF 文件
auto-aof-rewrite-percentage 100   # 当 AOF 文件大小增长到初始大小的百分比时触发重写
auto-aof-rewrite-min-size 64mb    # 触发重写的最小文件大小

1.3.1.2 命令行

命令行,主要通过CONFIG SET的方式。

例如:

# 启用 AOF
CONFIG SET appendonly yes

# 修改同步策略
CONFIG SET appendfsync everysec

# 手动触发 AOF 重写
BGREWRITEAOF

# 可以使用 CONFIG GET 命令查看当前的 AOF 配置
CONFIG GET appendonly
CONFIG GET appendfilename
CONFIG GET appendfsync
CONFIG GET auto-aof-rewrite-percentage
CONFIG GET auto-aof-rewrite-min-size

image-20240604135432755

1.3.2 AOF执行过程

AOF缓冲区 -> 内核缓冲区 -> 磁盘

image-20240604135957539

1.3.2.1 写入AOF缓冲区

每当 Redis 执行一条写操作命令(如 SETINCR 等)时,这条命令会被记录到 AOF 缓冲区 server.aof_buf 中。

这个缓冲区在内存中,用于暂时存放待写入 AOF 文件的命令。

1.3.2.2 写入内核缓冲区(延迟写)

Redis 将 AOF 缓冲区中的命令写入到 AOF 文件中,这个过程是通过系统调用 write() 实现的。

注意啊,这里是延迟写,数据实际上是写入到系统内核的缓存区中,还没有真正写入到磁盘。

可以看看我这篇文章:Linux-文件写入和文件同步

1.3.2.3 写入AOF文件(刷盘)

只write的话,数据还在内核缓冲区。

什么时候将数据从系统内核缓存区同步到磁盘,取决于 AOF 的 fsync 策略配置,就是配置文件里的那个配置项。

配置选项 描述 优点 缺点
appendfsync always 每次写操作都调用 fsync() 将数据同步到磁盘。这样可以确保每次写操作都被立即持久化,但性能开销较大。 最安全,数据丢失风险最低 性能开销最大,可能影响 Redis 性能
appendfsync everysec 每秒调用一次 fsync()。这种策略在性能和持久性之间取得了一个较好的平衡。如果在 fsync() 之前宕机,则可能丢失最近一秒内的数据。 性能和持久性之间的平衡 可能丢失最近一秒内的数据
appendfsync no 不主动调用 fsync(),由操作系统决定何时将数据从内核缓存区同步到磁盘。这种策略性能最好,但最不安全,因为数据丢失的窗口期最长。 性能最好,最小的写入开销 数据丢失的窗口期最长,最不安全

简单再总结下就是:

  • always

    主线程调用 write 执行写操作后,后台线程( aof_fsync 线程)立即会调用 fsync 函数同步 AOF 文件(刷盘),fsync 完成后线程返回,这样会严重降低 Redis 的性能(write + fsync)。

  • everysec

    主线程调用 write 执行写操作后立即返回,由后台线程( aof_fsync 线程)每秒钟调用 fsync 函数(系统调用)同步一次 AOF 文件(write+fsyncfsync间隔为 1 秒)

  • no

    主线程调用 write 执行写操作后立即返回,让操作系统决定何时进行同步,Linux 下一般为 30 秒一次(write但不fsyncfsync 的时机由操作系统决定)。

1.3.3 为啥执行命令后再AOF

关系型数据库(如 MySQL)通常都是执行命令之前记录日志(方便故障恢复),而 Redis AOF 持久化机制是在执行完命令之后再记录日志。

image-20240604161255657

  • 记录日志的时候可以不用再进行语法检查了
  • 不会阻塞命令执行
  • 易于恢复,AOF中的命令一定是成功执行过的

Redis

作为一个内存数据库,Redis的主要目标是提供极高的吞吐量和低延迟的读写操作。

因此,Redis优先保证命令的快速执行,并在执行后异步地将命令记录到AOF文件中,以确保整体性能。

MySQL

关系型数据库的设计目标之一是提供强一致性和事务支持,因此它们更注重数据的持久性和可靠性。

在执行命令之前记录日志(如MySQL的binlog)确保即使在系统崩溃时也能通过这些日志进行完整的事务恢复。

额,总结来说,就是redis的目标是快,舍弃了一致性,争取不阻塞。Mysql呢,强调的是数据一致性,阻塞了就阻塞了,数据安全最重要。

1.3.4 AOF重写

1.3.4.1 为什么需要AOF重写

Redis的AOF(Append Only File)重写机制(AOF rewrite)是为了防止AOF文件过度膨胀,从而优化Redis的启动时间和存储使用效率。

AOF 记录的是每个写操作的日志,因此如果对同一个键进行了多次写操作,这些操作都会被顺序地记录在 AOF 文件中,所以AOF(Append Only File)文件中可能包含一个键的多个历史操作信息,这个AOF文件肯定是比较大的。

我们多次对同一个键进行写操作,记录多条命令,可能只有最后一次操作的结果是有效的。

AOF重写的相关配置如下:

# 当现有 AOF 文件大小相对于上次重写后的大小增长超过一定百分比时触发重写。默认值是 100(即增长一倍
auto-aof-rewrite-percentage 100

# 最小 AOF 文件大小,以字节为单位,只有当 AOF 文件大小超过这个值时才会触发重写。默认值是 64MB。
auto-aof-rewrite-min-size 64mb

1.3.4.2 AOF重写是怎么做的

AOF 重写是一个有歧义的名字,实际上没有对原来的AOF文件进行操作。

AOF 重写子进程直接读取服务器现有的键值对,然后用一条命令去代替之前记录这个键值对的多条命令,生成一个新的文件后去替换原来的 AOF 文件,不涉及到原文件的读取。

image-20240604161915702

我在执行重写的期间,也会有命令来呀?

AOF 文件重写期间,Redis 维护一个 AOF 重写缓冲区,该缓冲区会在子进程创建新 AOF 文件期间,记录服务器执行的所有写命令。

当子进程完成创建新 AOF 文件的工作之后,服务器会将重写缓冲区中的所有内容追加到新 AOF 文件的末尾,使得新的 AOF 文件保存的数据库状态与现有的数据库状态一致。

最后,服务器用新的 AOF 文件替换旧的 AOF 文件,以此来完成 AOF 文件重写操作。

1.3.4.3 AOF文件的格式

aof文件的格式是文本化的,可以直接看到key的变化,以键yang111为例。

增加string类型的key:yang111

set yang111 (null)
*3  # 表示有3个参数
$3  # 第1个参数长度为3
set # 第1个参数的具体值(命令也算参数)
$7      # 第2个参数长度为7
yang111 # 第2个参数的具体值
$0  # 第3个参数 这里最开始的时候没设置值 所以没有了

修改yang111的值为666

set yang111 666
*3
$3
set
$7
yang111
$3
666

删除键yang111

del yang111
*2
$3
del
$7
yang111

1.4 混合持久化

由于 RDB 和 AOF 各有优势,于是,Redis 4.0 开始支持 RDB 和 AOF 的混合持久化。

默认关闭,可以通过配置项开启。

# AOF前导rdb
aof-use-rdb-preamble yes

如果把混合持久化打开,AOF重写时直接把 RDB 的内容写到 AOF 文件开头。

所以,混合持久化的触发时机为AOF重写时。

  • 优点

结合RDB和AOF 的优点,即可以RDB快速恢复,也降低了数据丢失的风险。

  • 缺点

AOF 里面的 RDB 部分是压缩格式不再是 AOF 格式,可读性较差。

3.Redis高可用

高可用性(High Availability,HA)主要是为了确保系统在面对故障、负载变化等情况下仍能持续提供服务。

Redis中,针对高可用场景,主要有以下3个模式。

机制 描述 优点 缺点 适用场景
主从复制 数据从主节点复制到一个或多个从节点 读写分离,提高性能
数据冗余,提高可用性和安全性
单点故障,需要手动故障转移。 需要读写分离,提高读性能的场景
哨兵 (Sentinel) 监控 Redis 实例,自动进行故障转移 自动化故障检测和转移
扩展性强,管理多个实例
配置复杂
网络分区可能导致脑裂问题
需要高可用性和自动故障转移的场景
集群 (Cluster) 数据自动分片和复制,支持分布式存储 高可用性,通过主从复制和自动故障转移
高扩展性,支持横向扩展
配置和管理复杂
对网络要求高
大规模分布式存储需求,需高可用和扩展性

高可用性需要解决的问题

  • 单点故障

    • 问题:如果只有一个 Redis 实例,当它发生故障时,整个系统将无法访问缓存数据,导致服务中断。
    • 解决方案:通过主从复制和哨兵机制,确保在主节点故障时可以快速切换到从节点,保持服务可用性。
  • 数据丢失

    • 问题:在节点故障或系统崩溃时,数据可能会丢失,导致系统无法恢复到最新状态。
    • 解决方案:通过主从复制和持久化机制(如 RDB 和 AOF),保证数据在多个节点上有副本,降低数据丢失的风险。
  • 负载均衡

    • 问题:单个 Redis 实例的性能有限,在高并发访问时可能成为瓶颈。
    • 解决方案:通过主从复制实现读写分离,从节点分担读负载;通过集群机制将数据分片到多个节点上,分散负载。
  • 自动故障恢复

    • 问题:当节点故障时,如果需要人工介入进行恢复,将增加系统停机时间。
    • 解决方案:通过哨兵机制实现自动故障检测和转移,减少人工干预,快速恢复服务。
  • 扩展性

    • 问题:随着数据量和访问量的增长,单个节点无法满足需求,需要增加更多的节点来扩展系统容量和性能。
    • 解决方案:通过集群机制,支持横向扩展,可以动态添加或移除节点,适应业务增长需求。

3.1 主从复制

主从复制是 Redis 高可用性的基础,通过复制机制,数据可以从主节点(Master)复制到一个或多个从节点(Slave)。

当主节点发生故障时,从节点可以作为备份数据源,并且可以提升为新的主节点。

3.1.1 实现

首先,显然生产环境是不能单机跑的,宕机了就GG,那么必然是需要2个以上的Redis做冗余的。

最简单的方式,就是两个机器同时跑,但是呢,同时跑的话,几个节点的数据不一样,怎么同步呢?

基于这个场景,Redis提出了主从复制的模式。

  • 数据一致性:只在主节点写数据,然后从节点只同步数据,这样数据就不会混乱。
  • 单点故障:手动重新选取一个节点当master

主从之间采用异步复制和读写分离的方式。

  • 主节点(master):可以进行读写操作
  • 从节点(replica):一般是只读

image-20240604201234843

3.1.2 配置方式

假设主节点的 IP 地址是 192.168.1.100,端口是 6379

1. 配置主节点

主节点不需要特殊的配置,只需启动一个 Redis 实例即可。假设 Redis 安装在 /etc/redis/ 目录下,配置文件为 redis.conf

# 启动主节点
redis-server /etc/redis/redis.conf

2. 配置从节点

2.1 配置文件

从节点需要配置为从主节点同步数据。

编辑从节点的配置文件 redis.conf,添加以下配置:

# 从节点配置目标主节点
replicaof 192.168.1.100 6379

# 如果主节点设置了密码,还需要在从节点配置文件中添加以下配置
masterauth password

启动从节点

redis-server /path/to/your/slave-redis.conf
2.2 命令方式

连接到从节点

redis-cli -h <slave-ip> -p <slave-port>

在 Redis CLI 中执行以下命令配置从节点:

SLAVEOF 192.168.1.100 6379
CONFIG SET masterauth your-master-password

3.验证主从复制配置

连接到主节点和从节点,分别执行以下命令验证配置。

3.1 主节点
INFO replication

输出类似以下内容,表明主节点配置正确:

# Replication
role:master
connected_slaves:1
slave0:ip=<slave-ip>,port=<slave-port>,state=online,offset=12345,lag=0
3.2 从节点
INFO replication

输出类似以下内容,表明从节点配置正确:

# Replication
role:slave
master_host:192.168.1.100
master_port:6379
master_link_status:up

4. 其他配置选项

可以根据需求配置一些高级选项,例如复制延迟、心跳间隔等:

repl-diskless-sync no             # 是否使用无盘复制
repl-diskless-sync-delay 5        # 无盘复制延迟
repl-ping-slave-period 10         # 从节点向主节点发送 PING 命令的间隔
repl-timeout 60                   # 复制超时时间

3.1.3 数据同步方式

1.全量数据同步-SYNC

Redis 在 2.8 版本之前都是基于 SYNC 命令执行全量同步。

SYNC 命令的格式非常简单,没有参数,直接发送 SYNC 即可。

SYNC

简单来说,就是:

  • slave加入后,找mater节点要RDB文件。
  • master节点构建RDB文件(构建期间新增的数据,写在复制缓冲区中)
  • slave收到RDB文件,重建缓存信息。
  • master节点将复制缓冲区的数据同步给slave节点。
  • 配置完成后,master和slave之间会保持持续的连接和同步,主节点的写命令会实时传送到从节点。

image-20240604205356806

现在有个问题,master节点在收到从节点的同步请求后,会构建rdb文件,这个构建期间的命令怎么办?

master 为每一个 slave 单独开辟一块replication buffer(复制缓存区)来记录 RDB 文件生成后 master 收到的所有写命令。

当 RDB 文件传输完成并从节点加载后,主节点会将复制缓冲区中的所有写命令发送给从节点。

关于这个复制缓冲区,常见的配置如下:

client-output-buffer-limit <client-type> <hard-limit> <soft-limit> <soft-seconds>

# <client-type>:客户端类型,可以是 normal、replica 或 pubsub。
# <hard-limit>:硬限制,客户端的输出缓冲区大小达到此值时,连接将立即被断开。
# <soft-limit> 和 <soft-seconds>:软限制,客户端的输出缓冲区在 soft-seconds 秒内超过此值时,连接将被断开。

例如:

# 这个配置表示普通客户端的输出缓冲区没有任何限制,不会因为输出缓冲区过大而被断开。
client-output-buffer-limit normal 0 0 0
# normal:普通客户端(非复制客户端和非发布/订阅客户端)。
# 硬限制:0,表示不设硬限制。
# 软限制:0,表示不设软限制。
# 软秒数:0,表示不设软限制时间。

# 这个配置表示复制客户端的输出缓冲区有 256MB 的硬限制和 64MB/60秒的软限制。
client-output-buffer-limit replica 256mb 64mb 60
# replica:复制客户端(从节点)。
# 硬限制:256mb,如果复制客户端的输出缓冲区大小达到 256MB,连接将立即被断开。
# 软限制:64mb,如果复制客户端的输出缓冲区在 60 秒内超过 64MB,连接将被断开。
# 软秒数:60,软限制时间为 60 秒。

# 这个配置表示发布/订阅客户端的输出缓冲区有 32MB 的硬限制和 8MB/60秒的软限制
client-output-buffer-limit pubsub 32mb 8mb 60
# pubsub:发布/订阅客户端。
# 硬限制:32mb,如果发布/订阅客户端的输出缓冲区大小达到 32MB,连接将立即被断开。
# 软限制:8mb,如果发布/订阅客户端的输出缓冲区在 60 秒内超过 8MB,连接将被断开。
# 软秒数:60,软限制时间为 60 秒。

SYNC存在如下问题:

  • slave 加载 RDB 的过程中不能对外提供读服务。
  • slave 和 master 断开连接之后(本来master一直发命令给slave就好了),slave重新连上master需要重新进行全量同步。

2.增量同步-PSYNC

上面提到,命令传播的过程中,如果出现网络故障导致连接断开,此时新的写命令将无法同步到从库,slave重新连上master需要重新进行全量同步。

PSYNC 命令是 Redis 2.8 版本引入的一种用于主从复制的同步命令,解决了这个问题。

在从节点重新连接到主节点时,只会把断开的时候没有发生的写命令同步给从库,避免不必要的全量同步。

PSYNC <replicationid> <offset>

# <replicationid>:也叫runId,主节点的复制 ID(Replication ID),用于唯一标识主节点。
# <offset>:从节点的复制偏移量(Offset),表示从节点已经接收到的主节点数据的位置。

怎么解决断开后的同步问题呢?

  • master:记录自己写入缓冲区的偏移量(master_repl_offset),主库向从库发送 N 个字节的数据时,将自己的复制偏移量上加 N。

    主节点会为每个从节点维护一个结构体(replication),该结构体包含从节点的复制偏移量、连接状态等信息。

  • slave:记录 master 的runId和自己的复制进度(偏移量slave_repl_offset),从库接收到主库发送的 N 个字节数据时,将自己的复制偏移量加上 N。

当redis节点启动时,会生成一个唯一的 runid

可以使用info命令查看。

image-20240604214330515

  • 初次同步:当从节点首次连接到主节点时,会通过 PSYNC ? -1 命令请求全量同步。主节点在响应中会返回其 runid 和当前的复制偏移量。
  • 部分重同步
    • 如果从节点与主节点的连接断开并重新连接,从节点会发送 PSYNC <replicationid> <offset> 命令,其中 replicationid 是主节点的 runidoffset 是从节点最后接收到的复制偏移量。
    • 主节点检查从节点提供的 replicationid 是否与自己的 runid 匹配。如果匹配,则根据偏移量决定是否可以进行部分重同步。

image-20240604211140277

那么,怎么找到两个offset之间的数据呢?

复制缓冲区(Replication Backlog)

复制缓冲区也称之为环形缓冲区,是用于支持主从复制中的部分重同步(Partial Resynchronization)。

环形缓冲区在设计上是一个固定大小的数组,写操作以循环的方式存储在其中。

当缓冲区写满时,新写操作会覆盖旧的写操作,这样可以有效利用内存,并确保主节点可以保存最近一段时间内的写操作记录。

环形缓冲区相关配置:

repl-backlog-size 1mb            # 设置复制缓冲区的大小
repl-backlog-ttl 3600            # 设置复制缓冲区的生存时间(秒)

PSYNC存在如下问题:

  • 主从切换或者主节点宕机等,runid和offset这些发生变化,依然需要进行全量同步。

3.增量同步-PSYNC2.0

3.1.4 常见问题

1.为什么主从全量复制使用 RDB 而不是 AOF?

Redis 在主从全量复制中使用 RDB 而不是 AOF 的主要原因在于 RDB 文件具有传输效率高、一致性好、恢复速度快和简单性高的优点。

尽管 AOF 在持久化方面具有优势,但在初次同步和全量复制的场景下,RDB 文件能够更高效地完成数据传输和同步过程。

因此,Redis 选择使用 RDB 文件进行主从全量复制,以确保高效和一致的数据同步。

2.主从复制下从节点会主动删除过期数据吗?

在 Redis 的主从复制机制下,从节点(slave)不会主动删除过期数据。过期数据的处理是由主节点(master)负责的,然后将删除操作同步给从节点。

但是,但是。

我们主节点设置10s后过期 -> 发送给从节点执行 -> 从节点设置10s后过期

从节点是收到数据报文后才执行的,是有可能比主节点晚一丢丢过期的。

这种场景,可以不考虑使用指定ttl时间的方式,改为使用这两个命令:

在指定时间过期,注意保持各节点时间一致。

  • EXPIREAT
  • PEXPIREAT

3.2 哨兵Sentinel

在主从模式的场景下,一旦 master 宕机,我们需要从 slave 中手动选择一个新的 master,同时需要修改应用方的主节点地址,整个过程需要人工干预,十分麻烦。

哨兵模式,提供以下功能:

  • 监控:监控所有 redis 节点(包括 sentinel 节点自身)的状态是否正常。
  • 故障转移:如果一个 master 出现故障,sentinel 会帮助我们实现故障转移,自动将某一台 slave 升级为 master,确保整个 Redis 系统的可用性。
  • 通知 :通知 slave 新的 master 连接信息,让它们执行 replicaof 成为新的 master 的 slave。
  • 配置提供 :客户端连接 sentinel 请求 master 的地址,如果发生故障转移,sentinel 会通知新的 master 链接信息给客户端。

3.2 集群Cluster