Redis设计与实现-数据持久化

数据持久化

  • 1、RDB持久化
    • [1.1 RDB文件的创建和载入](#1.1 RDB文件的创建和载入)
    • [1.2 自动间隔保存](#1.2 自动间隔保存)
    • [1.3 RDB文件结构](#1.3 RDB文件结构)
  • 2、AOF持久化
    • [2.1 AOF写入](#2.1 AOF写入)
    • [2.2 AOF载入](#2.2 AOF载入)
    • [2.3 AOF重写](#2.3 AOF重写)

如有侵权,请联系~

如有错误,也欢迎批评指正~

本篇文章大部分是来自学习《Redis设计与实现》的笔记

1、RDB持久化

RDB持久化功能所生成的RDB文件是一个经过压缩的文件,通过RDB文件可以还原数据。

1.1 RDB文件的创建和载入

生成RDB文件有两个命令:

  • save命令:阻塞Redis服务器进程,直到RDB文件创建完成之前不能处理任何其他命令
  • bgsave命令:创建一个子进程,由子进程负责RDB文件的创建,redis父进程正常处理其他命令

这两种方式底层都是调用rdbSave函数。

针对于RDB的载入,没有明确的命令。而是服务器在启动的时候就会自动检测RDB文件,自动载入。RDB文件的载入是通过rdbLoad函数完成,在载入的过程正,服务器一直是处于阻塞状态。

当redis正在执行bgsave命令的时候,不会阻塞服务器执行其他命令。但是不包含save、bgsave、bgrewriteaof命令。当redis正在执行bgsave命令的时候,如果服务端又收到了:

  • save命令:save命令会被拒绝,父子进程都会调用rdbsave方法,防止产生竞争
  • bgsave命令:bgsave命令也会被拒绝,也会同时调用rdbsave方法
  • bgrewriteaof命令:bgrewriteaof命令会被延迟到bgsave命令执行完执行。如果redis正在执行bgrewriteaof命令,收到bgsave命令会被服务器拒绝。bgrewriteaof和bgsave命令不能同时执行是因为创建两个子进程,存在大量的磁盘读写,影响性能。

1.2 自动间隔保存

除了使用命令保存RDB文件,配置文件中也会有相关的配置。默认配置:

c 复制代码
save 900 1      # 900 秒内至少有 1 个键被修改时触发快照
save 300 10     # 300 秒内至少有 10 个键被修改时触发快照
save 60 10000   # 60 秒内至少有 10000 个键被修改时触发快照

只要满足一个条件就会触发bgsave进行自动保存。上述配置对应于redisServer结构体中的:

c 复制代码
struct redisServer {
    ...
    struct saveparam *saveparams;  // 动态数组,存储所有 save 配置项
    // 修改计数器 
    long long dirty;
	//上一次执行保存的时间
	time_t lastsave;
    ...
};
struct saveparam {
    time_t seconds;  // 时间间隔(秒)
    int changes;     // 键修改次数
};

saveparam数组用来存储上述自动间隔保存RDB的配置数据。

dirty记录自从上次save或者bgsave命令之后服务器【所有数据库】进行修改的次数,lastsave记录上次save或者bgsave命令成功执行的时间。

这个是由serverCron函数【默认100ms执行一次】进行扫描,通过saveparam和dirty、lastsave一起来判断是否需要触发bgsave进行RDB保存。(serverCron函数不止是进行判断是不是需要进行持久化,还包括判断过期键)

1.3 RDB文件结构

当服务器读取到db_number之后,就会切换到相应的数据库。db_number会根据数据库号的大小占用不同的大小。

每个键值对的结构如下,如果这个键没有设置过期时间,则没有前两个字段:

在RDB文件存储数据的时候,只会存储真实的数据,不会存储redisObject这种元数据。

2、AOF持久化

与RDB持久化【将键值对数据保存到RDB文件】不同,AOF持久化是将写入命令按照请求协议格式resp保存到AOF文件中。

2.1 AOF写入

数据每次写入都会存储到aof_buf缓存中。

c 复制代码
struct redisServer {
	...
	// 持久化相关
    char *rdb_filename;        // RDB 文件路径
    int aof_state;             // AOF 状态
    sds aof_buf;               // AOF 缓冲区,存储到这里
    ....
}

服务端进程其实就是一个事件loop循环, 这个循环中包含:文件事件【执行请求并返回数据】、时间事件【如定时任务serverCron函数】。

在结束一个事件之前,都会调用flushAppendOnlyFile函数【决定是不是将aof缓存中的数据写入、同步到AOF文件中】。伪代码如下:

flushAppendOnlyFile同步的时机取决于服务器中appendfsync配置:always、everysec、no等配置。

这三种策略只是区分数据同步fsync到磁盘的时机,而写入命令是每次执行完都会将命令写入到aof_buf中和操作系统的缓存中,不区分上述配置。

2.2 AOF载入

服务器启动之后,会创建伪客户端【不带有网络连接的redis客户端】,因为redis命令需要在客户端的上下文中执行,并且载入的命令是来自于AOF文件,而不是网络连接,所以创建一个不带有网络连接的伪客户端。

然后从AOF文件中一条一条命令读取出来,伪客户端进行执行。

2.3 AOF重写

随着程序不断的运行,AOF文件会越来越大,占用磁盘空间并且当服务器重启进行数据加载的时候,AOF文件的载入也会需要很长时间。所以AOF重写就很有必要。

AOF重写其实就是将某一时刻的所有数据库的所有数据dump按照redis命令进行输出。例如

c 复制代码
rpush list "A" "B"
rpush list "C"
rpush list "D" "E" "F"
lpop list
rpop list
rpush list "G" "H"
// 如果此刻进行AOF重写,就会将上述执行的结果汇总为一条执行语句【不严谨,如果数据量太大可能分为多条语句,受这个参数影响REDIS.AOF_REWRITE_ITENS_PER_CM 】
// 重写AOF文件之后,AOF文件中只会记录这一条数据rpush list "B" "C" "D" "E" "G" "H" 而不是上述六条执行语句

AOF文件在重写的时候,会挨个遍历所有数据库,遍历每个数据库的每个键值对,并且还会过滤掉过期的键。

AOF重写是在子进程中执行的,并且使用了写时复制。在进行AOF重写期间,redis在进行执行写入命令的时候,除了执行命令、将命令写入到AOF缓冲区之外,还需要写入到AOF重写缓冲区中。

等AOF重写将所有数据库中的数据写完之后,子进程会给父进程一个信号,然后父进程:

  • 将AOF重写缓冲区中的命令写入到刚刚新的重写的AOF文件中,保证数据的一致性。
  • 原子性的将新的重写的AOF文件替换为原AOF文件。
相关推荐
小金三岁半1 小时前
云服务器安装redis
运维·服务器·redis
失业写写八股文1 小时前
Redis中keys命令的缺点
redis·后端
一朝入魔2 小时前
oracle 中创建 socket客户端 监听数据库变动,返回数据给服务端!!!
数据库·oracle
吧啦吧啦吡叭卜2 小时前
【打卡d5】快速排序 归并排序
java·算法·排序算法
JM丫2 小时前
python基础
笔记·python
大得3692 小时前
宝塔docker切换存储目录
java·docker·eureka
洛北辰南3 小时前
系统架构设计师—案例分析—数据库篇—分布式缓存技术
数据库·分布式·系统架构·缓存技术
东阳马生架构3 小时前
Netty基础—4.NIO的使用简介一
java·网络·netty
luckyext4 小时前
Postman用JSON格式数据发送POST请求及注意事项
java·前端·后端·测试工具·c#·json·postman
1 Byte4 小时前
Centos7使用docker搭建redis集群
redis·docker·容器·redis集群搭建