• Redis底层数据结构


      Redis以高性能著称,不仅其因为采用了单线程,IO多路复用,纯内存读写等等,它还在底层的数据结构实现上用了很大的心思。针对不同的场景采用了不同的数据结构,以提升数据读写效率,降低数据存储空间。

    Redis底层使用的六种数据结构

      

    Hash表

      首先Redis的底层实现采用的是Hash表数据结构,其实hash表本身就是一个数组,将key通过hash算法计算得出hash值对数组长度取模,用得到的值作为数组下标,然后把value保存在数组下标的位置。由于存储结构是数组,所以hash表的读复杂度是O(1)。

    应用场景

      1.Redis键值对的底层实现。

      2.当一个哈希键包含的键值对比较多,又或者键值对总的元素都是比较长的字符串时,Redis会使用hash表作为哈希键的底层实现。

      3.集合键的底层实现之一。

    哈希冲突问题

      当hash表的负载因子过大时,会频繁出现hash冲突的问题,不同的key经过hash计算和取模得到相同的数组下标。redis使用链表法,将数组下标相同的key使用链表串联起来,以解决哈希冲突的问题。但是当哈希冲突过多,链表长度过长时(需遍历链表,查找key完全匹配的数据),那么它的查询效率也将会降低到O(n),这个时候就要考虑增大数组的长度,执行rehash操作。

    渐进式rehash

        rehash操作需要创建一个新的长度*2的数组,将所有的key进行hash计算并对新的数组长度取模,然后把数据放在新的数组下标中,这样可以大幅度减少哈希冲突的比例,防止查询效率的退化。但是在生产系统中的数据量是很大的,如果一下子对所有的key进行rehash计算并迁移,那么会导致cpu负载过重,响应正常的对外提供服务。redis采用了渐进式的rehash方式,分批次的将所有key完成rehash,然后将指针引用到新的数组地址。

    链表

      Redis实现了自定义的双向链表数据结结构,在redis的底层被广泛应用。在链表中查询一个给定的值的复杂度是O(N),将一个新的节点插入到链表头部/尾部的复杂度是O(1)。

    应用场景

    • 链表是list键的底层实现之一,当一个列表键包含了数量比较多的元素,又或者列表中保存的元素都是比较长的字符串时,Redis就会使用链表作为列表键的底层实现。
    • 发布与订阅
    • 慢查询
    • 监视器等

    跳表

      跳表是一种有序的数据结构,它通过在每个节点中维护多个指向其他节点的指针,从而达到快速访问节点的目的。跳表支持平均O(logN),最坏O(N)复杂度的节点查找,在大部分情况下,跳表的效率可以和平衡树媲美,并且实现更为简单,所以有不少程序都是用跳表代替平衡树。

              

    应用场景

      redis使用跳跃表作为有序集合键的底层实现之一,如果一个有序集合包含的元素数量比较多,又或者有序集合中元素的成员是比较长的字符串时,Redis就会使用跳表来作为有序集合键的底层实现。

    整型集合

      整数集合时集合键的底层实现之一,当一个集合中只包含整数值元素,并且这个集合的元素数量不多时,Redis就会使用整数集合作为集合键的底层实现。

      整数集合的底层实现为数组,这个数组以有序,无重复的方式保存集合元素,在有需要时,程序会根据新添加元素的数据类型,改变这个数组的数据类型(升级操作)。

      升级操作为整数集合带来了操作的灵活性,并且尽可能的节约了内存。

      整数集合只支持升级操作,不支持降级操作。

    数据结构

    typedef struct intset {
        // 编码方式
        uint32_t encoding;
        // 集合包含的元素数量
        uint32_t length;
        // 保存元素的数组,虽然声明为 int8_t 类型,并不保存任何 int8_t 类型的值,真正的类型取决于 encoding 的值  
        int8_t contents[]; 
    } intset;

    升级图解

    压缩列表

      压缩列表是 Redis 为了节约内存而开发的, 由一系列特殊编码的连续内存块组成的顺序型(sequential)数据结构。一个压缩列表可以包含任意多个节点(entry), 每个节点可以保存一个字节数组或者一个整数值。

    应用场景 

      压缩列表时列表键和哈希键的底层实现之一。

      当一个列表键只包含少量列表项,并且每个列表项都是小整数值或长度较短的字符串时。

      当一个哈希键只包含少量的键值对,并且每个键值对都是小整数值或长度较短的字符串时。

    设计实现

     

      压缩列表在表头有三个字段:

    • zlbytes,记录整个压缩列表占用对内存字节数;

    • zltail,记录压缩列表「尾部」节点距离起始地址由多少字节,也就是列表尾的偏移量;

    • zllen,记录压缩列表包含的节点数量;

    • zlend,标记压缩列表的结束点,固定值 0xFF(十进制255)。

      在压缩列表中,如果我们要查找定位第一个元素和最后一个元素,可以通过表头三个字段的长度直接定位,复杂度是 O(1)。而查找其他元素时,就没有这么高效了,只能逐个查找,此时的复杂度就是 O(N) 了,因此压缩列表不适合保存过多的元素。

     

      压缩列表节点包含三部分内容:

    • prevlen,记录了「前一个节点」的长度;

    • encoding,记录了当前节点实际数据的类型以及长度;

    • data,记录了当前节点的实际数据;

     连锁更新

       压缩列表新增某个元素或修改某个元素时,如果空间不不够,压缩列表占用的内存空间就需要重新分配。而当新插入的元素较大时,可能会导致后续元素的 prevlen 占用空间都发生变化,从而引起「连锁更新」问题,导致每个元素的空间都要重新分配,造成访问压缩列表性能的下降。

  • 相关阅读:
    全天防晒大充电,让你夏日无烦恼 生活至上,美容至尚!
    让皮肤美白细致的七大DIY 生活至上,美容至尚!
    夏日防晒护肤经验心得与大家分享 生活至上,美容至尚!
    如何排毒,运动才是关键 生活至上,美容至尚!
    在厨房也能瘦身的几个动作 生活至上,美容至尚!
    人体排毒全攻略,想长寿就看这里 生活至上,美容至尚!
    让你瘦不停的23个小细节 生活至上,美容至尚!
    各类型男士皮肤的护肤秘籍 生活至上,美容至尚!
    Android学习笔记08:相对布局RelativeLayout
    Android学习笔记06:线性布局LinearLayout
  • 原文地址:https://www.cnblogs.com/jasonbourne3/p/16190111.html
Copyright © 2020-2023  润新知