Redis RDB文件中的数据类型存储方式
Redis RDB 文件概述
Redis 是一个开源的内存数据结构存储系统,可用于多种场景,如数据库、缓存和消息中间件。RDB(Redis Database)是 Redis 持久化的一种方式,它将 Redis 在内存中的数据集快照写入磁盘,以在 Redis 重启时快速恢复数据。
RDB 文件本质上是一个紧凑的二进制文件,记录了某一时刻 Redis 数据库中的所有键值对数据。Redis 通过 fork 子进程来进行 RDB 文件的生成,这样可以避免在持久化过程中阻塞主进程。当生成 RDB 文件时,Redis 会将当前内存中的数据以特定的格式写入文件,而这个格式就涉及到不同数据类型的存储方式。
字符串类型(String)的存储
在 Redis 中,字符串是最基本的数据类型。在 RDB 文件中,字符串类型的存储方式相对较为直接,但也有一些细节需要关注。
简单动态字符串(SDS)基础
Redis 内部使用简单动态字符串(Simple Dynamic String,SDS)来表示字符串。SDS 是 Redis 自定义的一种字符串结构,与传统的 C 语言字符串(以空字符 '\0' 结尾的字符数组)不同,SDS 不仅记录了字符串的内容,还额外记录了字符串的长度等信息。
SDS 的结构体定义如下(简化版):
struct sdshdr {
int len; // 已使用的长度
int free; // 剩余可用的长度
char buf[]; // 存储字符串内容的数组
};
RDB 文件中字符串的存储格式
在 RDB 文件中,字符串的存储首先会根据其长度采用不同的编码方式。
-
长度小于 20 字节的字符串:这类字符串会使用一种紧凑的编码方式。例如,如果字符串长度小于 64 字节,会直接在存储值的区域内以字节形式存储字符串内容,同时在前面的几个字节记录字符串的长度信息。具体来说,会使用 1 到 5 个字节来记录长度,具体取决于字符串长度。如果长度小于 254 字节,会使用 1 个字节记录长度;如果长度在 254 到 65535 字节之间,会使用 2 个字节记录长度,以此类推。
-
长度大于等于 20 字节的字符串:对于较长的字符串,Redis 会使用另外一种编码方式。它会先写入一个特殊的标识字节,来表示这是一个长字符串,然后再写入字符串的长度(通常是 4 字节或 8 字节,取决于字符串长度范围),最后写入字符串的实际内容。
以下是使用 Python 和 Redis 客户端库演示字符串存储的代码示例:
import redis
# 连接到 Redis 服务器
r = redis.Redis(host='localhost', port=6379, db = 0)
# 设置一个短字符串
r.set('short_string_key', 'hello')
# 设置一个长字符串
long_string = 'a' * 1000
r.set('long_string_key', long_string)
在上述代码中,我们分别设置了短字符串和长字符串。当 Redis 将这些键值对写入 RDB 文件时,会按照上述不同的编码方式存储。
哈希类型(Hash)的存储
哈希类型在 Redis 中用于存储字段和值的映射。在 RDB 文件中,哈希类型的存储需要考虑如何高效地存储多个字段值对。
哈希类型的内部数据结构
Redis 哈希类型在内存中使用两种数据结构来存储:压缩列表(ziplist)和哈希表(hashtable)。当哈希对象的元素个数较少且每个字段和值的长度都较短时,会使用压缩列表来存储,以节省内存空间;当元素个数较多或者字段值长度较长时,会使用哈希表来存储,以提高查找效率。
压缩列表是一种紧凑的、顺序存储的结构,它将多个元素连续存储在一起,通过特殊的编码方式记录每个元素的长度和内容。哈希表则是基于传统的哈希算法,通过哈希函数将键映射到不同的桶(bucket)中,以实现快速的查找和插入。
RDB 文件中哈希类型的存储格式
-
使用压缩列表存储的哈希:在 RDB 文件中,对于使用压缩列表存储的哈希,会先写入一个标识,表示这是一个基于压缩列表的哈希。然后会依次写入压缩列表中的每个元素,每个元素包括字段和值。字段和值的存储方式与字符串类似,根据长度采用不同的编码。
-
使用哈希表存储的哈希:当哈希使用哈希表存储时,RDB 文件会先写入一个标识,表明这是基于哈希表的哈希。接着会写入哈希表的大小(桶的数量),然后依次写入每个桶中的键值对。每个键值对的存储同样遵循字符串的存储编码方式。
以下是使用 Python 和 Redis 客户端库演示哈希类型存储的代码示例:
import redis
r = redis.Redis(host='localhost', port=6379, db = 0)
# 创建一个哈希
hash_key = 'example_hash'
r.hset(hash_key, 'field1', 'value1')
r.hset(hash_key, 'field2', 'value2')
在这个示例中,我们创建了一个哈希,并设置了两个字段值对。当 Redis 将这个哈希对象写入 RDB 文件时,会根据其元素数量和字段值长度决定采用压缩列表还是哈希表存储,并按照相应的格式写入文件。
列表类型(List)的存储
Redis 列表类型是一个有序的字符串元素集合。在 RDB 文件中,列表类型的存储需要考虑如何保持元素的顺序以及高效地存储多个元素。
列表类型的内部数据结构
Redis 列表在内存中同样使用两种数据结构来存储:压缩列表(ziplist)和快速列表(quicklist)。当列表元素个数较少且每个元素的长度都较短时,会使用压缩列表;当元素个数较多时,会使用快速列表。快速列表实际上是一个双向链表,每个节点是一个压缩列表,这样既可以利用压缩列表的紧凑性,又能在链表结构上高效地进行插入和删除操作。
RDB 文件中列表类型的存储格式
-
使用压缩列表存储的列表:在 RDB 文件中,对于使用压缩列表存储的列表,会先写入一个标识,表示这是一个基于压缩列表的列表。然后会依次写入压缩列表中的每个元素,元素的存储方式与字符串类似,根据长度采用不同的编码。
-
使用快速列表存储的列表:当列表使用快速列表存储时,RDB 文件会先写入一个标识,表明这是基于快速列表的列表。接着会写入快速列表的节点数量,然后依次写入每个节点中的压缩列表内容。每个节点中的压缩列表元素同样按照字符串的编码方式存储。
以下是使用 Python 和 Redis 客户端库演示列表类型存储的代码示例:
import redis
r = redis.Redis(host='localhost', port=6379, db = 0)
# 创建一个列表
list_key = 'example_list'
r.rpush(list_key, 'element1')
r.rpush(list_key, 'element2')
在上述代码中,我们向列表中添加了两个元素。当 Redis 将这个列表对象写入 RDB 文件时,会根据元素数量和长度决定采用压缩列表还是快速列表存储,并按照相应的格式写入文件。
集合类型(Set)的存储
Redis 集合类型是一个无序的、唯一的字符串元素集合。在 RDB 文件中,集合类型的存储需要保证元素的唯一性以及高效的查找和插入。
集合类型的内部数据结构
Redis 集合在内存中使用两种数据结构来存储:整数集合(intset)和哈希表(hashtable)。当集合中的所有元素都是整数且元素个数较少时,会使用整数集合存储,整数集合是一个有序的、紧凑的数组结构,专门用于存储整数类型的集合元素;当集合元素类型多样或者元素个数较多时,会使用哈希表存储,哈希表通过哈希函数将元素映射到不同的桶中,以实现快速的查找和插入。
RDB 文件中集合类型的存储格式
-
使用整数集合存储的集合:在 RDB 文件中,对于使用整数集合存储的集合,会先写入一个标识,表示这是一个基于整数集合的集合。然后会写入整数集合的编码方式(如 16 位、32 位或 64 位整数编码),接着写入集合中的元素数量,最后依次写入每个整数元素。
-
使用哈希表存储的集合:当集合使用哈希表存储时,RDB 文件会先写入一个标识,表明这是基于哈希表的集合。接着会写入哈希表的大小(桶的数量),然后依次写入每个桶中的元素。每个元素的存储同样遵循字符串的存储编码方式。
以下是使用 Python 和 Redis 客户端库演示集合类型存储的代码示例:
import redis
r = redis.Redis(host='localhost', port=6379, db = 0)
# 创建一个集合
set_key = 'example_set'
r.sadd(set_key, 'element1')
r.sadd(set_key, 'element2')
在这个示例中,我们向集合中添加了两个元素。当 Redis 将这个集合对象写入 RDB 文件时,会根据元素类型和数量决定采用整数集合还是哈希表存储,并按照相应的格式写入文件。
有序集合类型(Sorted Set)的存储
Redis 有序集合类型是一个有序的、唯一的字符串元素集合,每个元素都关联一个分数(score),用于决定元素的顺序。在 RDB 文件中,有序集合类型的存储需要考虑如何同时存储元素和分数,以及高效地维护元素的顺序。
有序集合类型的内部数据结构
Redis 有序集合在内存中使用两种数据结构来存储:压缩列表(ziplist)和跳跃表(skiplist)与哈希表的组合。当有序集合的元素个数较少且每个元素的长度都较短时,会使用压缩列表;当元素个数较多时,会使用跳跃表和哈希表的组合。跳跃表是一种基于链表的数据结构,通过在链表节点上增加多层指针,以实现快速的查找和插入操作,哈希表则用于快速定位元素,提高查找效率。
RDB 文件中有序集合类型的存储格式
-
使用压缩列表存储的有序集合:在 RDB 文件中,对于使用压缩列表存储的有序集合,会先写入一个标识,表示这是一个基于压缩列表的有序集合。然后会依次写入压缩列表中的每个元素,每个元素由分数和成员组成。分数和成员的存储方式与字符串类似,根据长度采用不同的编码。
-
使用跳跃表和哈希表存储的有序集合:当有序集合使用跳跃表和哈希表存储时,RDB 文件会先写入一个标识,表明这是基于跳跃表和哈希表的有序集合。接着会写入跳跃表的节点数量,然后依次写入每个跳跃表节点的分数和成员。同时,还会写入哈希表的相关信息,以确保元素的唯一性。每个分数和成员的存储同样遵循字符串的存储编码方式。
以下是使用 Python 和 Redis 客户端库演示有序集合类型存储的代码示例:
import redis
r = redis.Redis(host='localhost', port=6379, db = 0)
# 创建一个有序集合
sorted_set_key = 'example_sorted_set'
r.zadd(sorted_set_key, {'member1': 1,'member2': 2})
在上述代码中,我们向有序集合中添加了两个成员及其对应的分数。当 Redis 将这个有序集合对象写入 RDB 文件时,会根据元素数量和长度决定采用压缩列表还是跳跃表与哈希表组合存储,并按照相应的格式写入文件。
不同数据类型存储方式的优势与影响
-
空间利用:Redis 针对不同数据类型和数据量采用不同的存储结构和编码方式,极大地优化了空间利用。例如,对于小数据量且数据长度较短的情况,使用压缩列表可以有效减少内存占用;而对于大数据量或数据长度较长的情况,使用哈希表、跳跃表等结构虽然占用更多内存,但能提供更高效的操作。
-
读写性能:不同的存储方式对读写性能也有影响。以字符串存储为例,短字符串的紧凑编码方式使得读取速度更快,因为不需要额外的复杂解析;而哈希类型中,哈希表结构在查找和插入操作上性能较高,适合大数据量的哈希对象。
-
持久化与恢复:RDB 文件中数据类型的存储方式直接影响到持久化和恢复的效率。紧凑的二进制格式使得 RDB 文件在写入磁盘时更加高效,而在恢复数据时,Redis 可以根据存储格式快速解析并重建内存中的数据结构。
总结不同数据类型在 RDB 文件中的存储特点
- 字符串:根据长度采用不同编码,短字符串紧凑存储,长字符串先标识再存长度和内容。
- 哈希:根据元素数量和字段值长度选择压缩列表或哈希表存储,压缩列表紧凑存储字段值对,哈希表按桶存储键值对。
- 列表:依据元素数量和长度选择压缩列表或快速列表,压缩列表顺序存储元素,快速列表存储节点及节点内压缩列表。
- 集合:元素为整数且数量少用整数集合,否则用哈希表,整数集合存编码、数量和元素,哈希表存桶及元素。
- 有序集合:元素少且短用压缩列表,否则用跳跃表和哈希表组合,压缩列表存分数和成员,跳跃表和哈希表存节点、分数、成员及哈希表信息。
通过深入理解 Redis RDB 文件中不同数据类型的存储方式,开发人员可以更好地优化 Redis 的使用,提高系统的性能和可靠性。无论是在设计数据结构时选择合适的 Redis 数据类型,还是在进行数据持久化和恢复时,了解这些存储细节都能帮助开发人员做出更明智的决策。