Redis持久化RDB原理+伪代码实现

简介: Redis持久化RDB原理+伪代码实现

Redis持久化RDB原理+伪代码实现

Redis 分别提供了 RDBAOF 两种持久化机制, 本章首先介绍 Redis 服务器保存和载入 RDB 文件的方法,重点说明 SVAE 命令和 BGSAVE 命令的实现方式。之后,本章会继续介绍 Redis 服务器自动保存功能的实现原理。各个组成部分,并说明这些部分的结构和含义。在本章的最后,我们将对实际的 RDB 文件进行分析和解读,将之前学到的关于 RDB 文件的知识投人到实际应用中。其中还会查看有些伪代码方便理解,本文来源 redis设计与实现,关于 redis 持久化知识比较重要,所以直接看的书,避免走弯路,以这篇文章记录一下。

基本介绍

RDB 持久化既可以手动执行,也可以根据服务器配置选项定期执行,该功能可以将某个时间点上的数据库状态保存到一个 RDB 文件中。所生成的 RDB 文件是一个经过压缩的二进制文件,通过该文件可以还

原生成RDB文件时的数据库状态。

RDB文件的创建与载入

有两个 Redis 命令可以用于生成 RDB 文件,一个是 SAVE ,另一个是 BGSAVE

  • SAVE 命令会阻塞 Redis 服务器进程,直到 RDB 文件创建完毕为止,在服务器进程阻塞期间,服务器不能处理任何命令请求
  • BGSAVE 命令会派生一个子进程,然后由子进程负责创建 RDB 文件,服务器进程(父进程)继续处理命令请求

创建 RDB 文件的实际工作由 rdb.c/rdbsave 函数完成, SAVE 命令和 BGSAVE 命令会以不同的方式调用这个函数,通过以下伪代码可以明显地看出这两个命令之间的区别:

def SAVE():
    rdbSave()
def BGSAVE():
    pid = fork()
    if pid == 0:
        # 子进程保存 RDB
        rdbSave()
    elif pid > 0:
        # 父进程继续处理请求,并等待子进程的完成信号
        handle_request()
    else:
        # pid == -1
        # 处理 fork 错误
        handle_fork_error()

RDB 文件的载入工作是在服务器启动时自动执行的,载入 RDB 文件的实际工作由 rdb.c/rdbLoad 函教完成,所以 Redis 并没有专门用于载人 RDB 文件的命令,只要 Redis 服务器在启动时检测到 RDB 文件存在,它就会自动载入 RDB 文件。

image.png

看到以上输出就是在成功载入 RDB 文件打印的,另外值得一提的是,因为 AOF 文件的更新频率通常比 RDB 文件的更新频率高,所以:

  • 如果服务器开启了 AOF 持久化功能,那么服务器会优先使用 AOF 文件来还原数据库状态。
  • 只有在 AOF 持久化功能处于关闭状态时,服务器才会使用 RDB 文件来还原数据库状态。

下图是服务器载入文件时的判断流程:

image.png

SAVE和BGSAVE命令执行时服务器的不同状态

SAVE

前面提到过,当 SAVE 命令执行时, Redis 服务器会被阻塞,所以当 SAVE 命令正在执行时,客户端发送的所有命令请求都会被拒绝。

只有在服务器执行完 SAVE 命令、重新开始接受命令请求后,客户端发送的命令才会被处理。

BGSAVE

因为 BGSAVE 命令的保存工作是由子进程执行的,所以在子进程创建 RDB 文件的过程中, Redis 服务器仍然可以继续处理客户端的命令请求,但是,在 BGSAVE 命令执行期间,服务器处理 SAVEBGSAVEBGREWRITEAOF 三个命令的方式会和平时有所不同。

首先,在 BGSAVE 命令执行期间,客户端发送的 SAVE 命令会被服务器拒绝,服务器禁止 SAVE 命令和 BGSAVE 命令同时执行是为了避免父进程(服务器进程)和子进程同时执行两个 rdbSave函数 调用,防止产生竞争条件。

其次,在 BGSAVE 命令执行期间,客户端发送的 BGSAVE 命令会被服务器拒绝,因为同时执行两个 BGSAVE 命令也会产生竞争条件

最后, BGREWRITEAOFBGSAVE 两个命令不能同时执行:

  • 如果 BGSAVE 命令正在执行,那么客户端发送的 BGREWRITEAOF 命令会被延迟到 BGSAVE 命令执行完毕之后执行。
  • 如果 BGREWRITEAOF 命令正在执行,那么客户端发送的 BGSAVE 命令会被服务器拒绝。

因为 BGSAVEBGREWRITEAOF 两个命令的实际工作都由子进程执行,所以这两个命令在操作方面并没有什么冲突的地方,不能同时执行它们只是一个性能方面的考虑。并发出两个子进程,并且这两个子进程都同时执行大量的磁盘写人操作,这怎么想都不会是一个好主意。

服务器在载入RDB文件期间,会一直处于阻塞状态,直到载入工作完成

自动间隔性保存

这个就是利用 BGSAVE 命令,设置相关条件执行命令,例如我们 redis 一般有如下配置:

save 900 1
save 300 10
save 60 10000

以上配置的解释

  • 服务器在 900 秒之内,对数据库进行了至少 1 次修改
  • 服务器在 300 秒之内,对数据库进行了至少 10 次修改
  • 服务器在 60 秒之内,对数据库进行了至少 10000 次修改

自动保存伪代码

struct redisServer {
    // 记录保存条件的数组
    struct saveparam *saveparams;
    // 修改计数器
    long long dirty;
    // 上一次执行保存的时间
    time_t lastsave;
    // ....
}
struct saveparam {
    // 秒数
    time_t seconds;
    // 修改数
    int changes;
}

大概用图表示是这样

image.png

除了 saveparams 数组之外,服务器状态还维持著一个 dirty 计数器,以及一个 lastsave 属性

  • dirty 计数器记录距离上一次成功执行 SAVE 命令或者 BGSAVE 命令之后,服务器对数据库状态(服务器中的所有数据库)进行了多少次修改(包括写入、删除、更新等操作)。
  • lastsave 属性是一个 UNIX 时间戳,记录了服务器上一次成功执行 SAVE 命令或者 BGSAVE 命令的时间。

例如:

SET message "hello" # 程序此时将 dirty计数器增加1
SADD database Redis MongoDB MariaDB # 程序此时将 dirty计数器增加3

image.png

上图就展示了服务器状态中包含的 dirty 计数器和 lastsave 属性,说明如下:

  • dirty 计数器的值为 123 ,表示服务器在上次保存之后,对数据库状态进行了 123 次修改
  • lastsave 属性则记录了服务器上次执行保存操作的时间戳

检查保存条件是否满足

Redis 的服务器周期性操作函数 servercron 默认每隔100毫秒就会执行一次,该函条件是否已经满足,如果满足的话,就执行 BGSAVE 命令。

以下伪代码展示了 servercron 函教检查保存条件的过程:

def serverCron():
    # 遍历所有条件
    for saveparam in server.saveparams:
        # 计算距离上次执行保存操作有多少秒
        save_interval = unixtime_now() - server.lastsave
        # 如果数据库状态的修改次数超过条件所设置的次数 并且距离上次保存的时间超过条件所设置的时间 那么执行保存操作
        if server.dirty >= saveparam.changes and save_interval > saveparam.seconds:
            BGSAVE()

通过以上代码可以得知,程序会遍历并检查 saveparams 数组中所有保存条件,只要有任意一个条件被满足,那么服务器就会执行 BGSAVE 命令

RDB文件结构

以下展示了一个完整 RDB 文件所包含的各个部分

image.png

REDIS

RDB 文件的最开头是 REDIS 部分,这个部分的长度为5字节,保存著"REDIS"五个字符。通过这五个字符,程序可以在载入文件时,快速检查所载入的文件是否 RDB 文件。

db_version

db_version 一个四字节长的以字符表示的整数,记录了该文件所使用的 RDB 版本号。目前的 RDB 文件版本为 0009 。因为不同版本的 RDB 文件互不兼容,所以在读入程序时,需要根据版本来选择不同的读入方式。

databases

databases 部分包含着零个或任意多个数据库,以及各个数据库中的键值对数据:

  • 如果服务器的数据库状态为空(所有教据库都是空的),那么这个部分也为空,长度为0字节
  • 如果服务器的数据库状态为非空(有至少一个数据库非空),那么这个部分也为非空,根据数据库所保存键值对的数量、类型和内容不同,这个部分的长度也会有所不同。

EOF

EOF 常量的长度为1字节,这个常量标志着 RDB 文件正文内容的结束,当读入程序遇到这个值的时候,它知道所有数据库的所有键值对都已经载入完毕了

CheckSum

check_sum 是一个8字节长的无符号整数,保存着一个校验和,这个校验和是程序通过对 REDISdb_versiondatabasesEOF四个部分的内容进行计算得出的。服务器在载人RDB文件时,会将载人数据所计算出的校验和与 check_sum 所记录的校验和进行对比,以此来检查 RDB 文件是否有出错或者损坏的情况出现。

Version 5 开始,如果在配置文件中开启 rdbchecksum yes ,会在 RDB 文件的结尾处,用 8 个字节, CRC64 计算整个文件内容的检验和。

举个例子

这个是我最新拉的 redis ,数据为空,我们依次进行分析: od -c rdb.rdb

0000000   R   E   D   I   S   0   0   0   9 372  \t   r   e   d   i   s
0000020   -   v   e   r 005   6   .   0   .   9 372  \n   r   e   d   i
0000040   s   -   b   i   t   s 300   @ 372 005   c   t   i   m   e 302
0000060   M 301 264   _ 372  \b   u   s   e   d   -   m   e   m 302   @
0000100 345  \f  \0 372  \f   a   o   f   -   p   r   e   a   m   b   l
0000120   e 300  \0 377   g 311 203 274 200   T 211 376
0000134

实际上这些字段是 AOFRDB 通用部分的文件头内容:

  1. 头5字节固定为REDIS
  2. 第6~9共四字节为 RDB 版本号
  3. 接下来为 redis-ver 和它的值,即 redis 版本
  4. 接着 redis-bits 和它的值,即 redis 的位数,值为32或64
  5. 接着为 ctime 和它的值, RDB 文件创建时间
  6. 接着为 used-mem 使用内存大小
  7. 最后是 aof-preamble 和它的值,值为0或1,1表示RDB有效。

RDB 文件头在 aof-preamble 之前多了如下三项:

  • repl-stream-dbserver.master 客户端中选择的数据库
  • repl-id 当前实例 replication ID
  • repl-offset 当前实例复制的偏移量

你学会了

  • RDB 文件用于保存和还原 Redis 服务器所有数据库中的所有键值对教据。
  • SAVE 命令由服务器进程直接执行保存操作,所以该命令会阻塞服务器。
  • BGSAVE 令由子进程执行保存操作,所以该命令不会阻塞服务器。
  • 服务器状态中会保存所有用 save 选项设置的保存条件,当任意一个保存条件被潮足时,服务器会自动执行 BGSAVE 命令。
  • RDB 文件是一个经过压缩的二进制文件,由多个部分组成。
  • 对于不同类型的键值对, RDB 文件会使用不同的方式来保存它们。
目录
相关文章
|
存储 缓存 NoSQL
Redis 服务器全方位介绍:从入门到核心原理
Redis是一款高性能内存键值数据库,支持字符串、哈希、列表等多种数据结构,广泛用于缓存、会话存储、排行榜及消息队列。其单线程事件循环架构保障高并发与低延迟,结合RDB和AOF持久化机制兼顾性能与数据安全。通过主从复制、哨兵及集群模式实现高可用与横向扩展,适用于现代应用的多样化场景。合理配置与优化可显著提升系统性能与稳定性。
371 0
|
3月前
|
NoSQL 安全 关系型数据库
Redis:持久化的两种方式
Redis持久化机制主要包括RDB和AOF两种方式。RDB通过生成数据快照进行持久化,支持手动或自动触发,具有加载速度快、文件紧凑等特点,但无法实时保存数据。AOF则记录每个操作命令,保障数据更安全,支持多种写入策略,并可通过重写机制优化文件大小。两者各有优劣,常结合使用以兼顾性能与数据安全。
|
3月前
|
存储 缓存 NoSQL
Redis持久化深度解析:数据安全与性能的平衡艺术
Redis持久化解决内存数据易失问题,提供RDB快照与AOF日志两种机制。RDB恢复快、性能高,但可能丢数据;AOF安全性高,最多丢1秒数据,支持多种写回策略,适合不同场景。Redis 4.0+支持混合持久化,兼顾速度与安全。根据业务需求选择合适方案,实现数据可靠与性能平衡。(238字)
|
3月前
|
存储 缓存 监控
Redis分区的核心原理与应用实践
Redis分区通过将数据分散存储于多个节点,提升系统处理高并发与大规模数据的能力。本文详解分区原理、策略及应用实践,涵盖哈希、范围、一致性哈希等分片方式,分析其适用场景与性能优势,并探讨电商秒杀、物联网等典型用例,为构建高性能、可扩展的Redis集群提供参考。
197 0
|
6月前
|
存储 监控 NoSQL
流量洪峰应对术:Redis持久化策略与内存压测避坑指南
本文深入解析Redis持久化策略与内存优化技巧,涵盖RDB快照机制、AOF重写原理及混合持久化实践。通过实测数据揭示bgsave内存翻倍风险、Hash结构内存节省方案,并提供高并发场景下的主从复制冲突解决策略。结合压测工具链构建与故障恢复演练,总结出生产环境最佳实践清单。
199 9
|
9月前
|
NoSQL Redis
Redis的数据持久化策略有哪些 ?
Redis 提供了两种方式,实现数据的持久化到硬盘。 1. RDB 持久化(全量),是指在指定的时间间隔内将内存中的数据集快照写入磁盘。 2. AOF持久化(增量),以日志的形式记录服务器所处理的每一个写、删除操作 RDB和AOF一起使用, 在Redis4.0版本支持混合持久化方式 ( 设置 aof-use-rdb-preamble yes )
|
10月前
|
存储 NoSQL 安全
Redis的两种持久化方式---RDB、AOF
通过本文的介绍,我们详细讲解了Redis的两种主要持久化方式:RDB和AOF。每种方式都有其独特的优缺点和适用场景。在实际应用中,可以根据具体需求选择合适的持久化方式,或者同时启用RDB和AOF,以达到最佳效果。希望本文能帮助您更好地理解和应用Redis的持久化机制,构建高效、可靠的数据存储解决方案。
990 79
|
12月前
|
存储 NoSQL Redis
Redis 持久化揭秘:选择 RDB、AOF 还是混合持久化?
Redis 是一个内存数据库,意味着它主要将数据存储在内存中,从而能够提供极高的性能。然而,作为内存数据库,Redis 默认情况下的数据不会永久保存。为了确保数据在重启或故障后能够恢复,Redis 提供了几种 **持久化机制**。这些机制允许 Redis 将内存中的数据保存到硬盘上,从而实现数据持久化。
692 22
Redis 持久化揭秘:选择 RDB、AOF 还是混合持久化?
|
存储 缓存 NoSQL
大数据-45 Redis 持久化概念 RDB AOF机制 持久化原因和对比
大数据-45 Redis 持久化概念 RDB AOF机制 持久化原因和对比
211 2
大数据-45 Redis 持久化概念 RDB AOF机制 持久化原因和对比
|
存储 缓存 NoSQL
Redis中的rdb和aof
本文深入探讨了Redis的持久化机制,包括RDB和AOF两种方式。详细解释了RDB的工作原理、优势和劣势,以及AOF的实现原理、配置选项、文件重写机制和三种数据同步方式,还介绍了AOF文件修复工具redis-check-aof的使用,并通过实例展示了如何开启和配置AOF持久化方式。
Redis中的rdb和aof