Redis篇1——Redis深度剖析:从 5 种对象到 6 大底层结构

做后端开发这么久,Redis 肯定没少用。但你有没有想过,当我们敲下 SET name "Redis" 或者 ZADD rank 100 "Player1" 时,内存里到底发生了什么?

很多人会问:"Redis 底层是用 C 语言写的吗?"

答案是肯定的:是的,Redis 是由 C 语言编写的。正因为 C 语言可以直接操作内存,Redis 才能在性能上做到如此极致。

但有意思的是,Redis 并没有直接使用 C 语言自带的字符串或链表,而是自己造了一套轮子。这就引出了另一个核心概念:Redis 的"两层皮"架构

今天我们就来聊聊 Redis 的 5 种外层数据类型 到底是如何映射到 6 种底层数据结构 上的。


一、 为什么要有"两层皮"?

我们在写代码时,操作的是 Redis 对象(Object),也就是大家熟知的:String、List、Hash、Set、ZSet。

但在 Redis 内部,它并不会傻乎乎地只用一种方式存数据。它非常"鸡贼"(聪明),遵循一个核心原则:

数据少时,死抠内存(用紧凑型结构);数据多时,追求速度(用索引型结构)。

所以,理解 Redis 的关键,不在于死记硬背,而在于理解这种动态切换 的逻辑。我们将底层的核心结构归纳为 6 种(如果不算被淘汰的,主要就是这 6 大金刚)。


二、 6 大底层数据结构详解

1. SDS:简单动态字符串 (Simple Dynamic String)

  • 这是什么:它是 String 类型的基石。

  • 为什么要造轮子 :C 语言原本的字符串(以 \0 结尾)有两个大毛病:一是获取长度要遍历,慢;二是容易缓冲区溢出,不安全。

  • 长什么样:

    你就把它想象成一个带计数器的数组。
    Plaintext

    cpp 复制代码
    [ len: 5 | free: 2 | buf: "Redis\0.." ]

    它记录了当前长度 len,所以获取长度是 O(1) 的;而且它预分配了空间,防止频繁申请内存。

2. IntSet:整数集合

  • 这是什么 :当你用 SADD 往 Set 里只存整数,且数量不多时,Redis 会用这个。

  • 长什么样:

    这就是一个排序好的整数数组。
    Plaintext

    cpp 复制代码
    [1, 5, 9, 12, 20]

    因为是有序的,查找时使用二分查找,虽然比哈希表慢一点点,但非常非常省内存,因为没有指针开销。

3. Dict:字典 (哈希表)

  • 这是什么:这是 Redis 的骨架。Hash、Set、ZSet 的大数据存储都靠它。

  • 长什么样:

    标准的 数组 + 链表 结构,用来解决哈希冲突。

  • 它的绝活:渐进式 Rehash。

    它内部有两个哈希表 ht[0] 和 ht[1]。当需要扩容时,它不会一次性把所有数据搬过去(那样会卡死主线程),而是每次增删改查时顺手搬一点,后台再搬一点,不知不觉就完成了扩容。

4. ZipList / Listpack:压缩列表 / 紧凑列表

  • 这是什么:这是 Redis 省内存的神器。

  • 长什么样:

    别被"列表"两个字骗了,它在物理内存上其实是一整块连续的内存条(像数组)。
    Plaintext

    cpp 复制代码
    [总长度|尾偏移|元素1|元素2|元素3|结尾]

    它没有指针(指针在 64 位系统很占地),而是直接挨着存数据。

  • 重要更新

    • ZipList:老版本用的,有个致命缺陷叫"连锁更新"(改一个元素长度,后面全得跟着改)。

    • Listpack :Redis 7.0 引入的接班人。结构类似,但去掉了导致连锁更新的字段。现在新版 Redis 里的 Hash 和 ZSet 小数据底层都换成这个了。

5. QuickList:快速列表

  • 这是什么:List(列表)对象的专用底层。

  • 长什么样:

    它是"链表"和"压缩列表"的混血儿。

    普通的链表指针太多太费内存,ZipList 太长了插入又慢。QuickList 就在中间取了个平衡:

    宏观上是一个双向链表,但链表的每个节点里,存的不是一个数据,而是一整块 ZipList/Listpack。

6. SkipList:跳表

  • 这是什么:ZSet(有序集合)的灵魂。

  • 长什么样:

    你可以把它理解为**"多层级的高速公路"**。

    普通链表查找要从头遍历。跳表在链表上面加了几层索引(Level)。

    • L3: 1 -> 50 -> 100

    • L2: 1 -> 10 -> 20 -> ... -> 50

    • L1: 1 -> 2 -> 3 -> ...

      查找时,先走上层大步跳跃,快到了再下层精细查找。效率直逼二叉树,但实现简单得多。


三、 终极映射:对象 vs 结构

了解了这 6 大结构,我们最后把它们和 5 大对象对应起来,这就是面试的满分答案

如果你用这个命令... 数据少/小时 (省内存) 数据多/大时 (高性能)
String (SET) int (纯数字) SDS
List (LPUSH) QuickList QuickList
Hash (HSET) Listpack (旧:ZipList) Dict
Set (SADD) IntSet Dict
ZSet (ZADD) Listpack (旧:ZipList) Dict + SkipList

总结一下

Redis 的设计哲学其实就两句话:

  1. 能省则省:数据少的时候,用连续内存(Listpack, IntSet),没有指针开销。

  2. 该快则快:数据多的时候,上重型索引(Dict, SkipList),保证高并发下的响应速度。

搞懂了这 6 种结构和这种动态切换的逻辑,Redis 的底层对你来说就是透明的了。

相关推荐
小陈工14 小时前
Python Web开发入门(十七):Vue.js与Python后端集成——让前后端真正“握手言和“
开发语言·前端·javascript·数据库·vue.js·人工智能·python
科技小花18 小时前
数据治理平台架构演进观察:AI原生设计如何重构企业数据管理范式
数据库·重构·架构·数据治理·ai-native·ai原生
一江寒逸18 小时前
零基础从入门到精通MySQL(中篇):进阶篇——吃透多表查询、事务核心与高级特性,搞定复杂业务SQL
数据库·sql·mysql
D4c-lovetrain18 小时前
linux个人心得22 (mysql)
数据库·mysql
阿里小阿希19 小时前
CentOS7 PostgreSQL 9.2 升级到 15 完整教程
数据库·postgresql
荒川之神19 小时前
Oracle 数据仓库雪花模型设计(完整实战方案)
数据库·数据仓库·oracle
做个文艺程序员19 小时前
MySQL安全加固十大硬核操作
数据库·mysql·安全
不吃香菜学java19 小时前
Redis简单应用
数据库·spring boot·tomcat·maven
一个天蝎座 白勺 程序猿19 小时前
Apache IoTDB(15):IoTDB查询写回(INTO子句)深度解析——从语法到实战的ETL全链路指南
数据库·apache·etl·iotdb
不知名的老吴19 小时前
Redis的延迟瓶颈:TCP栈开销无法避免
数据库·redis·缓存