`

redis 数据结构分析

 
阅读更多

   REDIS底层数据结构

 一.redis的字符串

   redis底层使用SDS(simple dynamic string) 作为默认字符串表示.SDS还被用作缓冲区,AOF模块的缓冲区以及客户端状态中的输入缓冲区.

 

    1.1  SDS的定义    

struct sdshdr{
   int len;//字符串长度
   int free;//数组中未使用的长度
   char buff[];//字符数组
}

   

   与C语言的字符串相比,SDS获取字符串长度的时间复杂度为o(1),SDS还杜绝了缓冲区溢出的情况。

   

   1.2 SDS的空间分配优化策略

      

        1.2.1 空间预分配

            

          如果对SDS进行修改,长度小于1MB,那么程序分配和len属性同样大小的空间.如果大于1M,那么会分配1MB的未使用空间.

 

         1.2.2 惰性空间释放

 

  1.3 二进制安全

 

     C语言默认以空字符结尾,所以不能保存图片,音频等,SDS通过len属性来判断是否到结尾了,所以可以保存二进制数据.

 

             

二.redis的链表结构

 

    2.1 链表的数据结构

     

typedef struct  listNode{
    listNode *head;
    listNode *tail;
   unsigned long len;//节点数量
   void * dup(void * ptr)//复制函数
   void * free(void * ptr)//释放函数
   void * match(void * ptr,void * key)//对比函数
}

 

typedef struct  listNode{

   struct  listNode *prev;
   struct listNode * next;
   void * value;//节点的值
}

    


     列表被广泛用于实现redis的各种功能,比如列表键,发布于订阅,慢查询,监视器.

     

三.redis的字典结构(SET)

    字典使用哈希表作为底层的实现.

    3.1 哈希表的数据结构

               

typede struct dictht{
   dicEntry **table;//哈希表数组
   unsigned long size;//哈希表大小
   unsigned  long sizemark;//掩码用于计算索引值
   unsigned long used;//已有节点的数量
}

    table是一个数组,每个元素都指向一个dictEntry结构的指针,每个dicEntry保存一个键值对

   

       

        3.1.1 哈希表节点

          

typedef struct dictEntry{
    
    void *key;

    union{
      void *val;
      uint64_tu64;
      int64_ts64;

   }
   struct dictEntry * next;
}

       3.1.2 字典 

         

typede struct dict{

   dictType *type;
   void * privdata;//私有数据
   dicttht ht[2];//哈希表
    //rehash索引,当rehash不在进行时,值为-1
    int rehashidx;
}

     一般情况下,只用ht[0]哈希表,ht[1]只会在rehash时使用.rehashidx 记录目前rehash的进度.因为dictEntry节点组成的链表没有指向链表表尾的指针,所以总是把新节点添加到链表的表头位置.

 当哈希表的键值对过多或者过少,需要对哈希表进行rehash.

 

      为字典ht[1]分配空间

  • 如果是扩展操作,ht[1]的大小为第一个大于等于ht[0].used*2的2的n次方,
  • 如果是收缩操作,ht[1]的大小为第一个大于等于ht[0].used的2的n次方.
  • 将所有ht[0]的键值对rehash到ht[1]上
  • 当ht[0]的所有键值对都rehash完毕,释放ht[0]的空间,将ht[1]设置成ht[0],并在ht[1]创建一个空的哈希表. 
  •  

     

     

     

     
       3.1.3 rehash的条件
        当以下其中一个条件满足时,会进行rehash
  •    服务器目前没有在进行bgsave或者bgrewriteaof,并且负载因子大于等于1
  •    服务器目前在进行bgsave或者bgrewriteaof,并且负载因子大于等于5
           load_factor = ht[0].used/ht[0].size(负载因子 = 已保存的节点数量/哈希表大小).另一方面, 当负载因子小于0.1的时候,会采取收缩操作.
        3.1.4  渐进式rehash
           rehash并不是一次性完成的,而是分多次渐进式完成的.这样做的原因在于,如果数据了过大,可能导致服务器在一定时间内停止服务.
           以下是rehash的详细步骤:
  •    为ht[1]分配空间,让字典同时拥有两个哈希表
  •    把rehashidx值设置为0,表示正在进行rehash
  •    在rehash期间,对字典的操作都会映射到两个哈希表,同时还会顺带奖ht[0]哈希表在rehashidx索引上的所有键值对rehash到ht[1],rehash完成时,rehashidx自动加一.
  •    随着字典操作的不断执行,最终在某个时间点ht[0]的所有键值对会被全部rehash到ht[1]上,这时,rehashidx被设置成-1,表示rehash完成.

  •  

     

     

     

     
    四.redis的跳跃表(skiplist)
       跳表是一种有序的数据结构,采用了用空间换时间的思想来加快访问的速度.它主要用于有序集合键以及集群节点中用做内部结构. 

       4.1跳表的实现

        

   最左边的结构是zskiplist:

     header:指向跳表表头

     tail:指向跳表表尾

 .   level:代表跳表的层数.

     length:代表跳表的节点数量

   

 

  右边的代表zskiplistnode结构:

    层:节点中L1代表第一层,L2代表第二次,以此类推

    分值(score):各个节点的值,按从小到大排序

    成员对象(obj):o1,o2,o3是节点保存的成员变量

  

   3.2跳表的数据结构

 

typedef struct zskiplistNode {  
    robj *obj; //节点数据  
    double score;  
    struct zskiplistNode*backward; //后退指针  
    struct zskiplistLevel {  
        struct zskiplistNode*forward;//前进指针
        unsigned int span;//该层跨越的节点数量  
    } level[];  
} zskiplistNode;  
   
typedef struct zskiplist {  
    struct zskiplistNode*header, *tail;  
    unsigned long length;//节点的数目  
    int level;//目前表的最大层数  
} zskiplist;
 (PS:每个跳表节点的层高都在0-32之间)
  五.整数集合(intset)

   整数集合是集合键的底层实现之一,当一个集合只包含整数元素,并且数量不多的时候,会作为集合键的底层实现.



     

typedef struct intset {

    /*
 虽然 intset 结构将 contents 属性声明为 int8_t 类型的数组, 但实际上 contents 数组的真正类型取决于 encoding 属性的值:
如果 encoding 属性的值为 INTSET_ENC_INT16 , 那么 contents 就是一个 int16_t 类型的数组, 数组里的每个项都是一个 int16_t类型的整数值 
(最小值为 -32,768 ,最大值为 32,767 )。
如果 encoding 属性的值为 INTSET_ENC_INT32 , 那么 contents 就是一个 int32_t 类型的数组, 数组里的每个项都是一个 int32_t类型的整数值 
(最小值为 -2,147,483,648 ,最大值为 2,147,483,647 )。
如果 encoding 属性的值为 INTSET_ENC_INT64 , 那么 contents 就是一个 int64_t 类型的数组, 数组里的每个项都是一个 int64_t类型的整数值 
(最小值为 -9,223,372,036,854,775,808 ,最大值为9,223,372,036,854,775,807 )。
 */
    // 编码方式
    uint32_t encoding;
    // 集合包含的元素数量
    uint32_t length;
    // 保存元素的数组
    int8_t contents[];
} intset;

  如果新插入的元素比现有类型的encoding属性的值要长,就要进行升级.

  1.根据新元素的类型,重新计算并分配空间

  2.将所有元素都转换成新元素相同的类型,并重新放置.

  ps:整数集合不支持降级操作

 

六.压缩列表(ziplist)

  ziplist是列表键和哈希键的底层实现之一.如果数据量少并且都是小整数值或者长度较短的字符串,那么redis就用它作为列表键的底层实现.

    6.1 压缩列表的构成

    压缩列表是为了节约内存而开发的.一个压缩列表可以包含任意多个节点,每个节点保存一个字节数组或者一个整数值.

   

      

属性 类型 长度 用途
zlbytes uint32_t 4字节 计算整个压缩列表占用的内存字节数
zltail uint32_t 4字节 记录压缩列表表尾节点距离起始地址有多少字节:上图中p代表起始节点的指针,加上(0x3c=60)就是表尾节点的位置.
zllen uint16_t 2字节 记录节点数量,如果数量大于2^16,就需要遍历整个列表才能知道数量了.
entry   不定  
zlend uint8_t 1字节 (0xFF),用于标记压缩列表的末端

  

     6.2压缩列表节点的结构

     

  

 

属性 类型 长度 用途
previoud_entry_length   1~5个字节 前一个节点的长度
encoding   1,2,5字节 记录节点content属性保存的类型以及长度
content     保存节点内容
 
 

 

 

 

 

  

  • 大小: 29.1 KB
  • 大小: 47.5 KB
  • 大小: 68.8 KB
  • 大小: 84.2 KB
  • 大小: 163.7 KB
  • 大小: 277.1 KB
  • 大小: 330.7 KB
  • 大小: 253.6 KB
  • 大小: 281.8 KB
  • 大小: 218.5 KB
  • 大小: 217.5 KB
  • 大小: 226.3 KB
  • 大小: 225.9 KB
  • 大小: 232.6 KB
  • 大小: 204.7 KB
  • 大小: 118.2 KB
  • 大小: 39.5 KB
  • 大小: 49.2 KB
  • 大小: 31.9 KB
分享到:
评论

相关推荐

    Redis数据结构.pptx

    针对Redis的高级数据结构PPT。该PPT一共14页,介绍了Zset的数据结构类型,以及跳表的数据结构。简单阐述了BitMap,HLL,Bloom Filter的原理以及一些常用的指令。针对Bloom Filter有一些自己的见解以及分析

    redis数据结构基础知识及案列(每个数据结构一个案例).zip

    在【大学生 C/C++/JAVA/Python数据结构学习笔记和资料大全】中,你可以找到更多关于数据结构和算法的知识,这些是理解和使用Redis数据结构的基础。通过深入学习和实践,你不仅可以掌握Redis的使用,还能提升自己的...

    Redis基础数据结构.pptx

    详细分析redis设计及实现原理 详细分析redis设计及实现原理

    Redis技术分析及运用

    2. **数据类型丰富**:除了基本的键值对存储,Redis还支持更复杂的数据结构,如列表、集合、有序集合和哈希表,这些数据结构可以满足多种业务需求。 3. **持久化机制**:虽然Redis的主要工作方式是在内存中存储数据...

    redis底层数据模型与数据结构

    通过以上分析,我们可以了解到 Redis 的底层数据模型和数据结构是如何设计的,包括 `redisObject` 和 `redisDb` 的细节。这些结构的设计考虑到了内存高效利用、数据访问性能优化以及复杂命令的实现等方面的需求。...

    Redis源代码分析.pdf

    链表是Redis中最基本的数据结构,由adlist.h和adlist.c定义。基本数据结构listNode是最基本的结构,标示链表中的一个结点。结点有向前(next)和向后(prev)两个指针,链表是双向链表。保存的值是void*类型。 链表通过...

    cpp-RCT是一个通过解析rdb文件对redis内存结构分析的一站式平台

    通过cpp-RCT,你可以得到关于Redis数据的详细信息,包括键的类型、过期时间、占用的内存大小等,这对于优化Redis的性能和内存管理至关重要。 在cpp-RCT中,有以下几个核心功能: 1. **RDB文件解析**:cpp-RCT能够...

    redis内存存储结构分析

    ### Redis内存存储结构分析 #### 一、Redis内存存储总体结构概述 Redis是一种高性能的键值存储系统,它将所有数据存储在内存中,从而实现了非常快的数据读写速度。然而,这种设计也有其局限性,例如对于拥有大量...

    Go 实现的 Redis 内存分析工具

    3. **内存分析**:了解 Redis 数据结构(如 String、Hash、List、Set 和 Sorted Set)的内存占用情况,以及如何通过 Redis 命令获取相关统计信息。 4. **数据收集和排序**:收集每个 Key 的内存占用数据,根据大小...

    redis面试题之数据结构.zip

    在面试中,Redis的数据结构是考察候选人技术深度的重要部分。以下将详细介绍Redis中的主要数据结构及其应用。 1. 字符串(String) Redis中的字符串是最基本的数据类型,可以存储任何可打印的字符序列,包括整数和...

    【作者面对面问答】包邮送《Redis 5设计与源码分析》5本

    墨墨导读:本文节选自《Redis 5设计与源码分析》,主要为读者分析Redis高性能内幕,重点从源码层次讲解了Redis事件模型,网络IO事件重在使用IO复用模型,时间事件重在...系统讲解Redis 5设计、数据结构、底层命令实现,

    redis源码分析

    Redis支持多种数据结构,如字符串(String)、哈希表(Hash)、列表(List)、集合(Set)和有序集合(Sorted Set)。这些数据结构在源码中由不同的C结构体实现,例如`sds`用于表示字符串,`dict`表示哈希表,`list`...

    Redis学习实践 - 超实用超详细

    Redis数据结构、原理分析、应用实战 什么是Redis Redis的作用 Redis的存储结构 Redis的安装 Redis的数据类型 字符串类型 列表类型 hash类型 集合类型 有序集合 Redis原理分析 过期时间设置 过期删除的原理 发布订阅 ...

    Redis源代码分析

    以上是Redis源代码分析中关于基本数据结构的部分总结,通过这些数据结构的支持,Redis能够提供强大的功能和优异的性能。后续章节将进一步探讨Redis的服务器模型、虚拟内存、备份机制等高级特性。

    Redis内部数据结构详解(6)——skiplist1

    Redis中的跳跃列表(skiplist)是一种高效的数据结构,用于实现有序集合(sorted set)。它是一种概率性数据结构,通过随机概率控制层数,从而在平均情况下提供接近于对数时间复杂度的查找、插入和删除操作。skiplist...

    Redis架构设计分析

    #### 三、Redis数据持久化方案 Redis提供了两种数据持久化方式:RDB(Redis Database Backup)和AOF(Append Only File)。这两种方法各有优缺点,适用于不同的场景。 ##### RDB持久化方案 **RDB**持久化是指在...

    redis 数据类型详解 以及 redis适用场景场合

    Redis是一种高性能的键值存储系统,提供了多种数据结构的支持,使得它在不同的应用场景下都能够表现出色。接下来,我们将详细介绍Redis中的主要数据类型及其应用场景。 #### String 字符串 - **简介**:字符串是...

Global site tag (gtag.js) - Google Analytics