开发者

redis的底层数据结构详解

开发者 https://www.devze.com 2025-04-02 10:43 出处:网络 作者: nodelaynow
目录1. Redis数据结构层次1.1 外部数据结构1.2 内部数据结构2. redis底层实现2.1 String字符串类型的底层实现2.2 Hash类型的底层实现2.3 list类型的底层实现2.4 set类型的底层实现2.5 zset类型的底层实现总结redis性
目录
  • 1. Redis数据结构层次
    • 1.1 外部数据结构
    • 1.2 内部数据结构
  • 2. redis底层实现
    • 2.1 String字符串类型的底层实现
    • 2.2 Hash类型的底层实现
    • 2.3 list类型的底层实现
    • 2.4 set类型的底层实现
    • 2.5 zset类型的底层实现
  • 总结

    redis性能高的原因很大程度上是由于它的每一种数据结构都经过专门设计来应对不同场景

    redis的数据结构本质上可以分为两层,对外暴露的数据结构以及内部底层的数据结构

    1. redis数据结构层次

    1.1 外部数据结构

    对外暴露的数据结构即我们所使用的String,list,hash,set,zset等

    1.2 内部数据结构

    redis内部有更为精细的实现,总的来说有以下几种:

    SjavascriptDS,hashtable,ziplist,linkedlist,quicklist,intset,skiplist等

    2. redis底层实现

    2编程客栈.1 String字符串类型的底层实现

    字符串类型的底层实现主要依赖于SDS这种数据结构,相较于传统的C语言所表达的字符串,SDS内部作出改动,包含了len(字符串真实的长度),buf[](存放字符串数据的数组),alloc(buf数组所拥有的实际长度),flags。

    不同字符串长度会使用相同结构但不同长度的SDS数据结构(内部buf,alloc会不同),flags就是用来指定你用的是哪一种。

    SDS除开长度所导致的结构不同,具体的编码模式也会随着传值不同而不同,总的来说分为三种:RAW,EMBSTR,INT(写作int类型 但是实际上存储的是long)。

    当外部传来一个值为字符串类型的value时,redis会首先使用RAW接收,然后出于节省内存考虑,转化为INT或者EMBSTR类型的编码方式。

    这样的好处是,当我们试图对字符串进行increment这种操作时,int类型会自动直接加1,而str会试图转化为int,然后再加1,而当我们进行append这类操作时,int类型会试图转化为str再执行,而str会直接执行,不然编码方式不同,对于某些情况下的命令结果也会不同

    2.2 Hash类型的底层实现

    hash类型的底层实现主要分为两种,ziplist和hashtable

    ziplist是一块存放于连续内存的链表,基于内存连续,它的读取效率很高,寻找元素时直接遍历,内部不存储前后节点这类数据,但是也正因为内存连续,它本身也会收到一些限制,实际上ziplist要满足以下两种条件才会被使用

    • 长度小于512
    • 单个value小于64字节

    如不满足,强行使用,会导致涉及插入修改等发生的内存拷贝成本较大,影响性能。

    而不满足时,我们会使用hashtable来实现,哈希表里面的hash搜索能保证时间复杂度接近O(1),以链表数组的方式解决hash冲突,而当哈希表需要进行扩容时,为了避免单次扩容所导致的响应时间剧烈增加,它将单次扩容所需要的重哈希分散到后续对哈希表的增删改查操作里,一步步进行hash扩容。

    2.3 list类型的底层实现

    list类型在redis3.2之前使用ziplist或者linkedlist去实现,而在3.2之后使用quicklist去实现。

    quicklist是一种结合了zi编程plist和linkedlist两者的链表数据结构,内部每个节点即为一个ziplist,这样的设计实际上是对空间和时间的这种。

    对于一个双向列表而言,不仅需要保存内部数据,还要维护前后指针,占用空间;当列表长度过长时,也会产生碎片化问题,因此我们将部分数据封装在一个ziplist里,减少指针个数,并且减少内存脆片,当然ziplist本身也不宜设置过长,找不到足够大的连续空间给ziplist的话,存储效率也会下降。具体长度设置要取决于业务场景。

    2.4 set类型的底层实现

    set类型主要由hashtable和intset实现

    当内部数据全为整数并且内部数据量小于512个时,就使用intset去实现,intset本质上是一个由整数实现的有序集合,内部使用二分查找,和ziplist一样,也是一块连续的内存空间,并且针对证书大小也进行了不同的编码来实现对内存的优化。

    当不满足条件时,就会使用hashtable去实现,key即为存储数据,因为我们不关心value的值,所以value一般存储为null值。

    2.5 zset类型的底层实现

    zset类型由skiplist(跳表)来实现,以 1 2 3 4 5 6 7 8 9 10中查找7为例,按照链表而言,要一个一个遍历去查找,而对于跳表,每个节点内部会随机储存下一个目标以及跳过的层数,比如从1开始,跳到4,4<7,因此不需要关心1-4之间的数据编程客栈,4跳到8,8>7,因此回溯到4,4跳到6,然后6跳到8,发现8>7,因此回溯到6,6跳到7,这就是跳表的基础原理,每个节点内部会储存下一个节点以及能够跳跃的节点。

    那么是怎么设定每个节点能够跳跃到哪些节点的呢?

    每个节点在初始化时,会随机设置一个level数组来表明自身的层级(随机概率p由开发者设定),最下面一层存储下一个节编程点,第二层存储下一个拥有二层数组的节点,第三层存储下一个拥有三层数组的节点,最终打到尾节点上。

    总结

    以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程客栈(www.devze.com)。

    0

    精彩评论

    暂无评论...
    验证码 换一张
    取 消

    关注公众号