2)Redis 的键值对长什么样子,又是怎么存储的?

简介: 2)Redis 的键值对长什么样子,又是怎么存储的?

Redis 的数据类型可谓是 Redis 的精华所在,作为一款 QPS 能达到 10w 级别的内存数据库,具有如此高性能的原因有很多。除了所有的操作都在内存中进行之外,其数据类型的底层设计也起到了很大的作用。

我们知道 Redis 中有 5 种基础数据类型,分别是:String(字符串)、List(列表)、Hash(哈希)、Set(集合)和 ZSet(有序集合)。而为了支持这些数据类型,Redis 使用了多种数据结构来作为这些类型的底层结构。

这些数据结构是我们后续的重点,当然啦 Redis 的数据类型还不止这五种,还有几个更高级的类型,我们后续也会说,但最常用的还是上面五种。

不过这里我主要是想介绍一下 Redis 是怎么存储键值对的。

127.0.0.1:6379> SET name satori
OK
127.0.0.1:6379> LPUSH scores 90 95 93
(integer) 3
127.0.0.1:6379> HSET students satori 99
(integer) 1
127.0.0.1:6379>

这里的 name、scores、students 都属于 key,它们的 value 是不同的类型,那么问题来了,这些 key、value 存在什么地方呢?其实不难得出,既然 Redis 是键值对数据库,查询的效率又这么高,那么肯定是存在哈希表当中的,事实上也确实如此。

以上代码位于 Redis 源码的 src/server.c 文件中,代码里面的 server 就相当于 Redis 的服务端。

另外我们知道 Redis 默认有 16 个库,而 server.db[j] 就是获取 j 号库,所以此时相信你应该知道上面的代码所做的事情是什么了,就是为每一个库创建多个哈希表。而我们使用命令创建的 key、value 都会存储在 server.db[j].dict 里面,就是绿色框框里面创建的哈希表。

所以结论很清晰了,每一个库都有一个全局的哈希表 server.db[j].dict,专门负责存储设置的 key、value。其中 key 永远为 String 类型,value 可以是任意类型。这也侧面说明了我们设置的 key 不会重复,因为哈希表里面的 key 是不重复的。

一开始 hello 这个 key 不存在,那么会自动往全局的哈希表中加入一个键值对,key 为 hello,值的类型为 String。然后执行 lpush 的时候,由于 hello 这个 key 已存在,那么再执行就报错了。

现在我们知道 key、value 是存在哈希表里面了,但具体是怎么存储的呢?我们来看一张图:

一个哈希表可以是一个数组,数组里面的每一个存储单元都叫做哈希桶(Bucket),比如数组第一个位置(索引为 0)被编为哈希桶 0,第二个位置(索引为 1)被编为哈希桶 1,以此类推。

当我们写入一个键值对的时候,会根据 key 和 value 的指针构建一个 dictEntry 结构体实例。然后通过对 key 进行哈希运算来计算出桶的位置,最后将 dictEntry 结构体实例的指针写入哈希表中。

关于 Redis 哈希表的具体细节后续再聊,目前只需要知道 Redis 的 key、value 是存在哈希表中的即可。当然啦,更准确地说应该是 key、value 的指针所构建的 dictEntry 结构体实例的指针是存在哈希表当中的。

当我们通过 key 进行查找的时候,会先对 key 进行哈希运算,找到对应的哈希桶中存储的 dictEntry *,然后再根据 value 获取对应的值。

最后还需要特别补充一下,Redis 中所有的对象其实都是一个 redisObject 结构体实例,其结构如下:

不管是 String 对象、还是 List 对象、Hash 对象等等,它们其实都是一个 redisObject 对象,里面有一个 type 字段来标识这个对象的所属类型。

127.0.0.1:6379> type name
string
127.0.0.1:6379> type students
hash
127.0.0.1:6379> type scores
list

我们可以用 type 命令查看对象的类型,虽然显示的类型不同,但本质上它们都是 redisObject 这个结构体的实例对象。内部的 encoding 表示该类型的对象所使用的底层数据结构,ptr 表示指向底层数据结构的指针,而根据 encoding 和 ptr 的不同,type 可以是 string, list, hash 等等。

比如我们执行 sadd box 1 2 3 1,我们会说 box 的值是一个 Set 对象,这种说法是正确的。但是我们应该知道它其实是一个 redisObject,里面的 type 字段等于 "set",这背后的细节要搞清楚。不过后续我们还是会按照 String、List、Hash 之类的来称呼,就不用 redisObject 了,只要明白背后的关系就行。

如果你看过 Python 源代码的话,你会发现 redisObject 和 PyObject 在设计上有着异曲同工之妙。


小结



到目前为止,我们算是对 Redis 的键值对有了一个全局的认识。

Redis 内部有一个 redisObject,所有的键值对都是 redisObject 对象。只不过根据其内部 encoding 和 ptr 的不同,我们划分出了 String, List, Hash, Set, ZSet 等类型。永远是 String 类型,可以是任意类型,但不管哪一种类型,它们本质上都是 redisObject 对象。

下一篇文章我们就来详细解析这些数据类型是怎么实现的,先拿字符串开刀。

相关文章
|
2月前
|
存储 缓存 NoSQL
数据的存储--Redis缓存存储(一)
数据的存储--Redis缓存存储(一)
|
2月前
|
存储 缓存 NoSQL
数据的存储--Redis缓存存储(二)
数据的存储--Redis缓存存储(二)
数据的存储--Redis缓存存储(二)
|
2月前
|
消息中间件 缓存 NoSQL
Redis 是一个高性能的键值对存储系统,常用于缓存、消息队列和会话管理等场景。
【10月更文挑战第4天】Redis 是一个高性能的键值对存储系统,常用于缓存、消息队列和会话管理等场景。随着数据增长,有时需要将 Redis 数据导出以进行分析、备份或迁移。本文详细介绍几种导出方法:1)使用 Redis 命令与重定向;2)利用 Redis 的 RDB 和 AOF 持久化功能;3)借助第三方工具如 `redis-dump`。每种方法均附有示例代码,帮助你轻松完成数据导出任务。无论数据量大小,总有一款适合你。
78 6
|
4月前
|
存储 缓存 NoSQL
【Azure Redis 缓存】关于Azure Cache for Redis 服务在传输和存储键值对(Key/Value)的加密问题
【Azure Redis 缓存】关于Azure Cache for Redis 服务在传输和存储键值对(Key/Value)的加密问题
|
19天前
|
存储 NoSQL 算法
Redis分片集群中数据是怎么存储和读取的 ?
Redis集群采用哈希槽分区算法,共有16384个哈希槽,每个槽分配到不同的Redis节点上。数据操作时,通过CRC16算法对key计算并取模,确定其所属的槽和对应的节点,从而实现高效的数据存取。
44 13
|
19天前
|
存储 NoSQL Redis
【赵渝强老师】Redis的存储结构
Redis 默认配置包含 16 个数据库,通过 `databases` 参数设置。每个数据库编号从 0 开始,默认连接 0 号数据库,可通过 `SELECT <dbid>` 切换。Redis 的核心存储结构包括 `dict`、`expires` 等字段,用于处理键值和过期行为。添加键时需指定数据库信息。视频讲解和代码示例详见内容。
|
4月前
|
存储 消息中间件 NoSQL
Redis命令详解以及存储原理
Redis命令详解以及存储原理
|
4月前
|
存储 NoSQL Redis
Redis存储原理与数据模型
Redis存储原理与数据模型
|
4月前
|
存储 缓存 NoSQL
Redis深度解析:部署模式、数据类型、存储模型与实战问题解决
Redis深度解析:部署模式、数据类型、存储模型与实战问题解决