一文深度揭开Redis的磁盘持久化机制

redis 是内存数据库,数据都是存储在内存中,为了避免进程退出导致数据的永久丢失,需要定期将 Redis 中的数据以数据或命令的形式从内存保存到本地磁盘 。当下次 Redis 重启时,利用持久化文件进行数据恢复 。Redis 提供了 RDB 和 AOF 两种持久化机制,前者将当前的数据保存到磁盘,后者则是将每次执行的写命令保存到磁盘(类似于 MySQL 的 Binlog) 。本文将详细介绍 RDB 和 AOF 两种持久化方案,包括操作方法和持久化的实现原理 。
正文
Redis 是一个基于 K-V 存储的数据库服务器,下面先介绍 Redis 数据库的内部构造以及 K-V 的存储形式,有助于我们更容易理解 Redis 的持久化机制 。

一文深度揭开Redis的磁盘持久化机制

文章插图
 
1. Redis数据库结构
一个单机的 Redis 服务器默认情况下有 16 个数据库(0-15号),默认使用的是 0 号数据库,可以使用 SELECT 命令切换数据库 。
一文深度揭开Redis的磁盘持久化机制

文章插图
【一文深度揭开Redis的磁盘持久化机制】 
Redis 中的每个数据库都由一个 redis.h/redisDb 结构表示,它记录了单个 Redis 数据库的键空间、所有键的过期时间、处于阻塞状态和就绪状态的键、数据库编号等等 。
typedef struct redisDb { // 数据库键空间,保存着数据库中的所有键值对 dict *dict; // 键的过期时间,字典的键为键,字典的值为过期事件 UNIX 时间戳 dict *expires; // 正处于阻塞状态的键 dict *blocking_keys; // 可以解除阻塞的键 dict *ready_keys; // 正在被 WATCH 命令监视的键 dict *watched_keys; struct evictionPoolEntry *eviction_pool; // 数据库编号 int id; // 数据库的键的平均 TTL,统计信息 long long avg_ttl;} redisDb;复制代码由于 Redis 是一个键值对数据库(key-value pairs database), 所以它的数据库本身也是一个字典,对应的结构正是 redisDb 。其中,dict 指向的是一个记录键值对数据的字典,它的键是一个字符串对象,它的值则可以是字符串、列表、哈希表、集合和有序集合在内的任意一种 Redis 类型对象 。expires 指向的是一个用于记录键的过期时间的字典,它的键为 dict 中的数据库键,它的值为这个数据库键的过期时间戳,这个值以 long long 类型表示 。
一文深度揭开Redis的磁盘持久化机制

文章插图
 
2. RDB持久化
RDB 持久化(也称作快照持久化)是指将内存中的数据生成快照保存到磁盘里面,保存的文件后缀是 .rdb 。rdb 文件是一个经过压缩的二进制文件,当 Redis 重新启动时,可以读取 rdb 快照文件恢复数据 。RDB 功能最核心的是 rdbSave 和 rdbLoad 两个函数, 前者用于生成 RDB 文件并保存到磁盘,而后者则用于将 RDB 文件中的数据重新载入到内存中:
一文深度揭开Redis的磁盘持久化机制

文章插图
 
RDB 文件是一个单文件的全量数据,很适合数据的容灾备份与恢复,通过 RDB 文件恢复数据库耗时较短,通常 1G 的快照文件载入内存只需 20s 左右 。Redis 提供了手动触发保存、自动保存间隔两种 RDB 文件的生成方式,下面先介绍 RDB 的创建和载入过程 。
 
2.1. RDB的创建和载入
2.1.1. 手动触发保存
Redis 提供了两个用于生成 RDB 文件的命令,一个是 SAVE,另一个是 BGSAVE 。而触发 Redis 进行 RDB 备份的方式有两种,一种是通过 SAVE 命令、BGSAVE 命令手动触发快照生成的方式,另一种是配置保存时间和写入次数,由 Redis 根据条件自动触发保存操作 。
1. SAVE命令
SAVE 是一个同步式的命令,它会阻塞 Redis 服务器进程,直到 RDB 文件创建完成为止 。在服务器进程阻塞期间,服务器不能处理任何其他命令请求 。
  • 客户端命令
127.0.0.1:6379> SAVEOK复制代码
  • 服务端日志
6266:M 15 Sep 2019 08:31:01.258 * DB saved on disk复制代码执行 SAVE 命令后,Redis 在服务端进程(PID为6266)执行了 SAVE 操作,这个操作发生期间会一直阻塞 Redis 客户端的请求处理 。
 
2. BGSAVE命令
BGSAVE 是一个异步式的命令,和 SAVE 命令直接阻塞服务器进程的做法不同,BGSAVE 命令会派生出一个子进程,由子进程负责创建 RDB 文件,服务器进程(父进程)继续处理客户的命令 。
  • 客户端命令
127.0.0.1:6379> BGSAVEBackground saving started复制代码
  • 服务端日志
6266:M 15 Sep 2019 08:31:22.914 * Background saving started by pid 62836283:C 15 Sep 2019 08:31:22.915 * DB saved on disk6266:M 15 Sep 2019 08:31:22.934 * Background saving terminated with success复制代码


推荐阅读