内存型数据库Redis,是如何实现持久化的?

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: 内存型数据库Redis,是如何实现持久化的?

内存型数据库Redis,是如何实现持久化的?

一、导读


Redis是内存数据库,它将字节的数据库状态存储在内存里面,所以如果不想办法将存储在内存里的数据库状态保存到磁盘中,那么Redis服务器进程一旦退出,Redis中的数据库状态也会消失不见…


总所周知,Redis实现持久化主要有两种方式——RDB和AOF,本文主要介绍RDB。


RDB持久化既可以手动执行,也可以根据服务器配置选项定期执行,该功能可以将某个时间点上的数据库状态保存到一个RDB文件中,如图1:

20200903172024964.png

RDB持久化所生成的RDB文件是一个经过压缩的二进制文件 ,通过该文件可以还原生成RDB文件的数据库状态,如图2:

20200903172230355.png

本文主要介绍:


Redis服务器保存和载入RDB文件的方法,分析save命令和bgsave命令的实现

Redis服务器自动保存功能的实现原理

分析RDB文件的组成部分以及其结构和含义


二、RDB文件的创建与载入

Redis的save和bgsave命令用于生成RDB文件。

1.SAVE命令

save命令会阻塞Redis服务器进程,直到RDB文件创建完毕为止,在服务器进程阻塞期间,服务器不能处理任何命令请求。

redis> SAVE //等待直到RDB文件创建完毕
OK

2.BGSAVE命令

bgsave命令会派生出一个子进程,然后由子进程负责创建RDB文件,服务器进程(父进程)继续处理命令请求。

redis> BGSAVE //派生子进程,并由子进程创建RDB文件
Background saving started

两个命令创建RDB文件的实际工作由rdb.c/rdbSave函数完成,以下是SAVE和BGSAVE的伪码

public static void save(){
  rdbSave();
}
public static void bgSave(){
  PID pid = fork();//创建子进程
  if(pid == 0){
    rdbSave();//子进程负责创建RDB文件
    signal_parent();//完成后向父进程发送信号
  }else if(pid > 0){
    handle_request_and_wait_signal();//父进程继续处理命令请求,并通过轮询等待子进程的信号
  }else{
    handle_fork_error();//处理出错情况
  }
}

RDB文件会在Redis服务器启动的时候载入,但要注意是否有AOF文件。载入RDB文件是由rdb.c/rdbLoad函数完成。

20200904093132957.png

3.服务器状态

接下来通过表格来说明save、bgsave和载入RDB文件时的Redis服务器状态


image.png


三、自动间隔性保存

由于bgsave命令可以在不阻塞服务器的情况下生成RDB文件,所以用户可以通过设置服务器配置的save选项,让服务器每隔一段时间自动执行一次bgsave命令。

例如:

save 900 1
save 300 10
save 60 10000

只要满足以下三个条件的任意一个,BGSAVE命令就会被执行:

  • 服务器在900秒之内,对数据库进行了至少1次修改
  • 服务器在300秒之内,对数据库进行了至少10次修改
  • 服务器在60 秒之内,对数据库进行了至少10000次修改
1.设置保存条件

当配置文件设置了以下值后:

save 900 1
save 300 10
save 60 10000

服务器状态中的保存条件的数据结构如下:

20200905114612413.png

从这个数据结构可以看出,saveparams是Redis服务器的一个数组,数组中的每个元素都是一个saveparam对象,saveparam对象又有两个属性分别为seconds(秒数)和changes(修改数)


2.dirty计数器和lastsave属性

dirty计数器记录距离上一次成功执行save或bgsave命令之后,服务器对数据库状态进行了多少次修改。

lastsave属性是一个UNIX时间戳,记录了服务器上一次成功执行save或bgsave的时间。

3.检查保存条件是否满足

Redis的服务器周期性操作函数serverCron默认每隔100毫秒就会执行一次,该函数用于对正在运行的服务器进行维护,其负责检查save选项所设置的保存条件是否已经满足,满足则执行bgsave命令。


四、RDB文件结构

20200905135741778.png

这里主要讲下databases、EOF和check_sum的含义:


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


若服务器中所有数据库为空,则databases 也为空,长度为0字节

若服务器中至少有一个数据库非空,则databases 部分的长度会与数据库所保存的键值对的数量、类型和内容有关。

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


check_sum 是一个8字节的无符号整数,保存着一个校验和,其根据REDIS、db_version、databases、EOF四个部分的内容进行计算得出的。服务器在载入RDB文件时,会将载入数据所计算出的校验和与check_sum所记录的校验和进行对比,以此来检查RDB文件是否出错或损坏。


1.databases部分

一个RDB文件的databases部分可以保存任意多个非空数据库。


此RDB文件保存了0号和6号数据库中的所有键值对数据


REDIS db_version database 0 database 6 EOF check_sum

而每个非空数据库在RDB文件中都保存以下三个部分

SELECTDB db_number key_value_pairs

SELECTDB 常量的长度为1字节,当程序读到这个值时,它知道接下来要读入的将是一个数据库号码。

db_number 保存着一个数据库号码,长度为1、2或5字节,当程序读入db_number部分后,服务器会调用select命令,根据读入的数据库号码进行数据库切换,使得之后读入的键值对可以载入到正确的数据库中。

key_value_pairs部分保存数据库的所有键值对。若键值对带有过期时间,则过期时间也会和键值对保存在一起。

例如:0号数据库中的结构:

SELECTDB 0 key_value_pairs

对应于本节开始给出的RDB文件,补充完整如下图:

REDIS db_version SELECTDB 0 pairs0 SELECTDB 6 paris6 EOF check_sum


2.key_value_pairs部分

key_value_pairs由以下三部分组成:

TYPE key value


YPE记录了value的类型,长度1字节,值可以是以下其中一个:


REDIS_RDB_TYPE_STRING

REDIS_RDB_TYPE_LIST

REDIS_RDB_TYPE_SET

REDIS_RDB_TYPE_ZSET

REDIS_RDB_TYPE_HASH

REDIS_RDB_TYPE_LIST_ZIPLIST

REDIS_RDB_TYPE_SET_INTSET

REDIS_RDB_TYPE_ZSET_ZIPLIST

REDIS_RDB_TYPE_HASH_ZIPLIST

key是一个字符串对象,其编码方式和REDIS_RDB_TYPE_STRING类型的value一样。


根据TYPE类型的不同以及保存内容长度的不同,保存value的结构和长度也不同。

带有过期时间的key_value_pairs结构如下:

EXPIRETIME_MS ms TYPE key value

EXPIRETIME_MS 常量的长度为1字节,用于告知读入程序,接下来要读入一个以毫秒为单位的过期时间。

ms是一个8字节长的带符号整数,记录一个以毫秒为单位的UNIX时间戳即为k-v对的过期时间。


3.value编码

RDB文件中的每个value部分都保存了一个值对象,每个值对象的类型都由与之对应的TYPE记录,根据类型的不同,value部分的结构、长度也会不同。

接下来是各种不同类型的值对象在RDB文件中的保存结构。


(1)字符串对象


如果TYPE值为REDIS_RDB_TYPE_STRING ,那么value保存的值就是一个字符串对象,字符串对象的编码可以是REDIS_ENCODING_INT或REDIS_ENCODING_RAW。


若字符串对象编码为REDIS_ENCODING_INT,则对象中保存的是长度不超过32位的整数。


若字符串对象编码为REDIS_ENCODING_RAW,则有两种方法保存此字符串:


若字符串长度小于等于20字节,则这个字符串会被直接原样保存

若字符串长度大于20字节,则这个字符串会被压缩后保存


对于未被压缩的字符串,保存结构为:

len String

String保存字符串本身,len保存字符串的长度。

对于压缩后的字符串,保存结构为:

REDIS_RDB_ENC_LZF compressed_len origin_len compressed_string


REDIS_RDB_ENC_LZF常量表示字符串被LZF算法压缩。

当服务器读取到REDIS_RDB_ENC_LZF时,会根据之后的compressed_len 、origin_len、compressed_string对字符串进行解压缩。

其中compressed_string记录被压缩后的字符串,compressed_len 记录压缩后的长度,origin_len记录字符串原始长度。


(2)列表对象

如果TYPE值为REDIS_RDB_TYPE_LIST,那么value保存的值就是一个


REDIS_ENCODING_LINKEDLIST编码的列表对象,如:

list_length item1 item2 itemN

例如这是一个包含三个元素的列表:

3 5 “hello” 5 “world” 1 “!”

(3)集合对象

如果TYPE值为REDIS_RDB_TYPE_SET,那么value保存的值就是一个REDIS_ENCODING_HT编码的集合对象,如:

set_size item1 item2 itemN


例如这是一个包含四个元素的集合:

4 5 “apple” 6 “banana” 3 “cat” 3 “dog”


(4)哈希表对象

如果TYPE值为REDIS_RDB_TYPE_HASH,那么value保存的值就是一个REDIS_ENCODING_HT编码的集合对象,如:

hash_size k-v 1 k-v 2 k-v N

例如,这是一个包含两个键值对的哈希表

2 1 “a” 5 “apple” 1 “b” 6 “banana”


(5)有序集合对象

如果TYPE值为REDIS_RDB_TYPE_ZSET,那么value保存的值就是一个REDIS_ENCODING_SKIPLIST编码的集合对象,如:

sorted_set_size elem1 elem2 elemN


例如,这是一个带有两个元素的有序集合

2 2 “pi” 4 “3.14” 1 “e” 3 “2.7”


说明:


第一个元素的成员是长度为2的字符串“pi”,分值被转换成字符串之后变成了长度为4的字符串“3.14”

第二个元素的成员是长度为1的字符串“e”,分值被转换成字符串之后变成了长度为3的字符串“2.7”

(6)INTSET编码的集合

如果TYPE值为REDIS_RDB_TYPE_SET_INTSET,那么value保存的值就是一个整数集合对象,保存方法是:先将整数集合转换为字符串,然后将这个字符串保存到RDB文件里。


(7)ZIPLIST编码的列表、哈希表或有序集合

如果TYPE值为REDIS_RDB_TYPE_LIST_ZIPLIST、REDIS_RDB_TYPE_HASH_ZIPLIST、REDIS_RDB_TYPE_ZSET_ZIPLIST,那么value保存的就是一个压缩列表对象,RDB文件保存此对象的方法是:将压缩列表转成一个字符串对象,然后将这个字符串保存到RDB文件里。


五、参考文献与总结

参考文献:黄健宏——《Redis设计与实现》

Redis的RDB持久化功能无论是在工程应用上还是在后端开发工程师的面试上都是很重要的知识点,本文是我根据《Redis设计与实现》关于RDB的介绍时的学习总结,希望大家多多支持,如果觉得写得还OK的话,麻烦素质三连!!!

相关文章
|
21天前
|
NoSQL 安全 关系型数据库
Redis:持久化的两种方式
Redis持久化机制主要包括RDB和AOF两种方式。RDB通过生成数据快照进行持久化,支持手动或自动触发,具有加载速度快、文件紧凑等特点,但无法实时保存数据。AOF则记录每个操作命令,保障数据更安全,支持多种写入策略,并可通过重写机制优化文件大小。两者各有优劣,常结合使用以兼顾性能与数据安全。
|
运维 NoSQL 测试技术
Redis:内存陡增100%深度复盘
本文深度分析了Redis内存陡增100%的一些细节和解决方案。
268 1
Redis:内存陡增100%深度复盘
|
14天前
|
存储 缓存 NoSQL
工作 10 年!Redis 内存淘汰策略 LRU 和传统 LRU 差异,还傻傻分不清
小富带你深入解析Redis内存淘汰机制:LRU与LFU算法原理、实现方式及核心区别。揭秘Redis为何采用“近似LRU”,LFU如何解决频率老化问题,并结合实际场景教你如何选择合适策略,提升缓存命中率。
155 4
|
14天前
|
存储 缓存 NoSQL
Redis持久化深度解析:数据安全与性能的平衡艺术
Redis持久化解决内存数据易失问题,提供RDB快照与AOF日志两种机制。RDB恢复快、性能高,但可能丢数据;AOF安全性高,最多丢1秒数据,支持多种写回策略,适合不同场景。Redis 4.0+支持混合持久化,兼顾速度与安全。根据业务需求选择合适方案,实现数据可靠与性能平衡。(238字)
|
4月前
|
存储 监控 NoSQL
流量洪峰应对术:Redis持久化策略与内存压测避坑指南
本文深入解析Redis持久化策略与内存优化技巧,涵盖RDB快照机制、AOF重写原理及混合持久化实践。通过实测数据揭示bgsave内存翻倍风险、Hash结构内存节省方案,并提供高并发场景下的主从复制冲突解决策略。结合压测工具链构建与故障恢复演练,总结出生产环境最佳实践清单。
121 9
|
8月前
|
存储 NoSQL 安全
Redis的两种持久化方式---RDB、AOF
通过本文的介绍,我们详细讲解了Redis的两种主要持久化方式:RDB和AOF。每种方式都有其独特的优缺点和适用场景。在实际应用中,可以根据具体需求选择合适的持久化方式,或者同时启用RDB和AOF,以达到最佳效果。希望本文能帮助您更好地理解和应用Redis的持久化机制,构建高效、可靠的数据存储解决方案。
615 79
|
6月前
|
存储 NoSQL Redis
阿里面试:Redis 为啥那么快?怎么实现的100W并发?说出了6大架构,面试官跪地: 纯内存 + 尖端结构 + 无锁架构 + EDA架构 + 异步日志 + 集群架构
阿里面试:Redis 为啥那么快?怎么实现的100W并发?说出了6大架构,面试官跪地: 纯内存 + 尖端结构 + 无锁架构 + EDA架构 + 异步日志 + 集群架构
阿里面试:Redis 为啥那么快?怎么实现的100W并发?说出了6大架构,面试官跪地: 纯内存 + 尖端结构 +  无锁架构 +  EDA架构  + 异步日志 + 集群架构
|
7月前
|
NoSQL Redis
Redis的数据持久化策略有哪些 ?
Redis 提供了两种方式,实现数据的持久化到硬盘。 1. RDB 持久化(全量),是指在指定的时间间隔内将内存中的数据集快照写入磁盘。 2. AOF持久化(增量),以日志的形式记录服务器所处理的每一个写、删除操作 RDB和AOF一起使用, 在Redis4.0版本支持混合持久化方式 ( 设置 aof-use-rdb-preamble yes )
|
7月前
|
SQL 存储 缓存
【赵渝强老师】达梦数据库的内存结构
本文介绍了达梦数据库管理系统的内存结构,包括内存池、缓冲区、排序区和哈希区。内存池分为共享内存池和运行时内存池,能够提高内存申请与释放效率,并便于监控内存使用情况。缓冲区涵盖数据缓冲区、日志缓冲区、字典缓冲区和SQL缓冲区,用于优化数据读写和查询性能。排序区和哈希区分别提供排序和哈希连接所需的内存空间,通过合理配置参数可提升系统效率。文内附有具体配置示例及视频讲解,帮助用户深入理解达梦数据库的内存管理机制。
177 0
|
10月前
|
存储 NoSQL Redis
Redis 持久化揭秘:选择 RDB、AOF 还是混合持久化?
Redis 是一个内存数据库,意味着它主要将数据存储在内存中,从而能够提供极高的性能。然而,作为内存数据库,Redis 默认情况下的数据不会永久保存。为了确保数据在重启或故障后能够恢复,Redis 提供了几种 **持久化机制**。这些机制允许 Redis 将内存中的数据保存到硬盘上,从而实现数据持久化。
536 22
Redis 持久化揭秘:选择 RDB、AOF 还是混合持久化?

热门文章

最新文章