Redis那些数据结构到底是怎么实现的,今天咱们来好好聊聊原理和细节
- 问答
- 2026-01-19 17:18:56
- 4
说到Redis,大家肯定都知道它快,像个内存里的超快键值对仓库,但光说键值对就太笼统了,它里面存的“值”可不是简单的字符串那么简单,它支持好几种数据结构,比如字符串(String)、列表(List)、哈希(Hash)、集合(Set)、有序集合(Sorted Set)这些,这些数据结构就是Redis这么强大又好用的秘密武器,那今天咱们就钻进去看看,这些数据结构在Redis的肚子里到底是怎么长出来的,为啥能这么快。
最基础的字符串(String)
别看它叫字符串,它可是万能选手,能存文本、数字、甚至是图片的二进制数据,它的实现反而是最需要动脑筋的,Redis没有直接用C语言里那个以\0结尾的字符数组,而是自己搞了一个叫简单动态字符串(SDS) 的东西。
这个东西厉害在哪呢?根据《Redis设计与实现》这本书里说的,SDS结构里除了存字符串本身,还带了三个宝贝:一个是当前字符串的实际长度(len),一个是分配给这个字符串的总空间大小(alloc),还有一个是标志位(flags用来表示SDS的类型)。
这么干有几个天大的好处:
- 获取长度是闪电速度:C语言里要得到一个字符串的长度得从头开始数,直到碰到
\0,这叫O(N)的复杂度,而SDS直接读len属性,瞬间完成,是O(1)复杂度。 - 杜绝缓冲区溢出:C语言里拼接字符串如果没检查空间,很容易就把相邻的内存给覆盖了,很危险,SDS在拼接前会检查
alloc够不够,不够就自动扩容,安全得很。 - 减少内存分配次数:SDS用了空间预分配和惰性空间释放两种策略,比如你给一个SDS追加内容,它不光分配刚好够的空间,还会多分配一点预留着,下次再追加可能就不用再申请内存了,同样,字符串缩短时,多出来的空间先不急着还给操作系统,而是留着下次可能再用,这一来二去,频繁修改字符串的性能就上来了。
String类型能这么高效,全靠SDS这个打底的技术。
双剑合璧的列表(List)

List就是链表,支持从左边或右边插入弹出元素,咱们常用的LPUSH、RPOP命令就是干这个的,在Redis的早期版本,当列表元素比较少或者每个元素都是小整数时,它用一种叫ziplist(压缩列表) 的结构来存,这玩意儿说白了就是一块连续的内存,一个紧挨着一个地存放元素,非常节省内存,但它的缺点是修改起来效率不高,因为内存是连续的,插入或删除一个元素可能需要移动后面所有的元素。
当数据量变大时,Redis就自动把ziplist转换成标准的双向链表,这样插入删除就很快了,但缺点是比较耗内存,因为每个节点除了存数据,还要存指向前一个节点和后一个节点的指针。
Redis在背后自动帮我们做这个转换,用小数据时省内存,用大数据时保性能,非常智能。
双层结构的哈希(Hash)
Hash就像Java里的HashMap或者Python里的dict,可以存字段和对应的值,比如存一个用户信息,key是user:1000,value是一个哈希结构,里面包含name、"张三"、age、25这样的键值对。
它的实现和List的思路有点像,也是看情况用两种结构,当哈希表里的字段数量不多,且每个字段名和字段值都是小字符串时,Redis同样使用ziplist来存储,所有的字段和值一个接一个地线性存放。

一旦数据量超过了设定的阈值,它就会升级成一个真正的哈希表(字典),这个哈希表使用拉链法来解决哈希冲突(就是两个key算出来的哈希值一样时,把它们用链表连在一起),这个过程对用户是完全透明的,你感觉不到,但Redis在底层已经完成了优化。
确保唯一的集合(Set)
Set的最大特点就是里面的元素不能重复,适合用来存标签、好友ID这类数据,它的底层实现也有两副面孔。
当集合里全是整数且元素不多时,Redis直接用一种叫intset(整数集合) 的结构,它也是一块连续的内存,按顺序存放整数,查找可以用二分法,非常高效且节省内存。
如果存的是字符串,或者整数数量太多了,intset就扛不住了,Redis会把它转换成一个哈希表,不过这个哈希表有点特殊,它只有key,没有value(或者说value被设置成了NULL),因为集合的核心是判断“存在与否”,有key就代表存在,不需要value。
带排名的有序集合(Sorted Set)

这是Redis里最复杂但也最强大的数据结构,它像Set一样保证元素唯一,但每个元素都关联一个分数(score),可以根据分数来排序。
它的实现堪称一绝,用了两种数据结构组合起来干活,它用一个跳跃表(skiplist) 来存储元素和分数的关系,跳跃表可以理解为一种多层链表,最底层是所有元素按顺序排好的链表,上面几层是索引层,这样在查找时可以从最高层索引开始,大步跳跃着找,效率非常高,接近二分查找,而且实现起来比平衡树简单。
光有跳跃表还不够,它同时还用一个哈希表(字典)来存储元素到分数的映射关系,为啥要两个呢?你想啊,如果光用跳跃表,我要查某个特定元素的分数(比如ZSCORE命令),就得在跳跃表里遍历查找,但有了哈希表,我就能以O(1)的速度直接拿到这个元素的分数,跳跃表负责按分数范围快速操作(比如ZRANGEBYSCORE),哈希表负责按元素名快速查分数,两者互补。
和Hash、List一样,当元素数量较少时,有序集合也会先用ziplist来紧凑存储,超过限制再转换成跳跃表+哈希表的组合模式。
总结一下
你看,Redis的这些数据结构都不是省油的灯,它们在底层玩了很多花样:
- 因地制宜:一种数据类型根据数据量和内容,可能对应多种底层实现,小数据用紧凑的ziplist/intset省内存,大数据用哈希表/跳跃表保性能。
- 空间换时间:像SDS的预分配、哈希表的额外空间,都是为了用多一点内存来换取极致的操作速度。
- 组合拳:最典型的就是有序集合,用跳跃表和哈希表两种结构强强联合,应对各种查询场景。
正是这些精心设计和细节优化,让Redis在简单的键值对接口之下,隐藏了一个高效而复杂的引擎,成就了其高性能的声誉,理解了这些,你再使用Redis的各种命令时,心里就更有底了。
本文由盈壮于2026-01-19发表在笙亿网络策划,如有疑问,请联系我们。
本文链接:https://www.haoid.cn/wenda/83787.html
