• redis优势以及数据结构相关知识


    1、Redis有了解吗

    Redis (remote dictionary server)是一款基于内存存储的分布式数据库,支持持久化操作和多种数据类型,因为基于内存存储所以运行速度非常快,Redis还支持事务,事务中的命令会被序列化按照顺序执行,不会被其他客户端发送过来的命令所打断;


    2、为什么使用Redis,Redis的优势是什么

    1、性能极高
    Redis能读的速度是110000次/s,写的速度是81000次/s。

    2、丰富的数据类型
    Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储。

    3、原子性
    Redis的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性,通过MULTI和EXEC指令包起来。
    丰富的特性- Redis还支持publish/subscribe,通知, key过期等等特性。

    4、支持数据的持久化
    Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。

    5、支持数据的备份
    Redis支持数据的备份,即master-slave模式的数据备份。


    redis基本数据结构

    1.string

    使用一种叫简单动态字符串(SDS)的数据类型来实现。

    /*  
     * 保存字符串对象的结构  
     */  
    struct sdshdr {  
        int len;  // buf 中已占用空间的长度  
        int free;  // buf 中剩余可用空间的长度
        char buf[];  // 数据空间  
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    SDS 相比C 字符串的优势:

    SDS保存了字符串的长度,而C字符串不保存长度,需要遍历整个数组(找到’\0’为止)才能取到字符串长度。
    修改SDS时,检查给定SDS空间是否足够,如果不够会先拓展SDS 的空间,防止缓冲区溢出。C字符串不会检查字符串空间是否足够,调用一些函数时很容易造成缓冲区溢出(比如strcat字符串连接函数)。
    SDS预分配空间的机制,可以减少为字符串重新分配空间的次数。

    在这里插入图片描述


    2.list

    使用双向链表来实现。

    在这里插入图片描述


    3.hash

    hash结构里其实是一个字典,有许多的键值对(类似于python的dict类型)。

    redis的哈希表是一个dictht结构体:

    typedef struct dictht {
       dictEntry **table;//哈希表数组   
       unsigned long size;//哈希表大小  
       unsigned long sizemask;//哈希表大小掩码,用于计算索引值
       unsigned long used;//该哈希表已有节点的数量
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在这里插入图片描述

    哈希表节点的结构体如下:

    typeof struct dictEntry{  
       void *key;//键
       union{  //不同键对应的值的类型可能不同,使用union来处理这个问题
          void *val;
          uint64_tu64;
          int64_ts64;
       }
       struct dictEntry *next;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    其中解决哈希冲突的方法是拉链法。

    为了让哈希表的装载因子维持在一个合理的范围之内,需要对哈希表的大小进行扩展或者收缩,这叫做rehash。字典中总共有两个哈希表dictht结构体,ht[0]用来存储键值对,ht[1]用于rehash时暂存数据,平时它指向的哈希表为空,需要扩展或者收缩ht[0]的哈希表时才为它分配空间。

    比如扩展哈希表,就是为ht[1]分配一块大小为ht[0]两倍的空间,然后把ht[0]的数据通过rehash的方式全部迁移到ht[1],最后释放ht[0],使ht[1]成为ht[0],再为ht[1]分配一个空哈希表。收缩哈希表类似。

    渐进式rehash:redis并不是专门找时间一次性地进行rehash,而是渐进地进行,rehash期间不影响外部对ht[0]的访问,要求修改字典时要把对应数据同步到ht[1]中,全部数据转移完成时,rehash结束。


    4.set

    set可以用intset或者字典实现。

    intset:只有当数据全是整数值,而且数量少于512个时,才使用intset,intset是一个由整数组成的有序集合,可以进行二分查找。

    在这里插入图片描述
    字典

    不满足intset使用条件的情况下都使用字典(拉链法),使用字典时把value设置为null。

    在这里插入图片描述


    5.zset

    zset中的每个元素包含数据本身和一个对应的分数(score)。

    经典例子:一个zset的key是"math",代表数学课的成绩,然后可以往这个key里插入很多数据。输入数据的时候,每次需要输入一个姓名和一个对应的成绩。那么这个姓名就是数据本身,成绩就是它的score。

    zset的数据本身不允许重复,但是score允许重复。

    zset底层实现原理:

    1.数据少时,使用ziplist:ziplist占用连续内存,每项元素都是(数据+score)的方式连续存储,按照score从小到大排序。ziplist为了节省内存,每个元素占用的空间可以不同,对于大的数据(long long),就多用一些字节来存储,而对于小的数据(short),就少用一些字节来存储。因此查找的时候需要按顺序遍历。ziplist省内存但是查找效率低。

    2.数据多时,使用字典+跳表:

    在这里插入图片描述

    字典用来根据数据查score,跳表用来根据score查找数据(查找效率高)。

    理论上来讲,查找、插入、删除以及迭代输出有序序列这几个操作,红黑树也可以完成,时间复杂度和跳表是一样的。

    redis使用跳表而不是红黑树的原因:

    1.按照区间查找数据这个操作,红黑树的效率没有跳表高。跳表可以在 O(logn)时间复杂度定位区间的起点,然后在原始链表中顺序向后查询就可以了。

    2.相比于红黑树,跳表还具有代码更容易实现、可读性好、不容易出错、更加灵活等优点。

    3.插入、删除时跳表只需要调整少数几个节点,红黑树需要颜色重涂和旋转,开销较大。

    时间复杂度:时间复杂度 = 索引的层数 * 每层索引遍历元素的个数。

    首先看索引层数,假设每两个节点抽一个出来作为上一级索引的结点,而且最高一级索引有3个节点,则索引层数为log2(n)。

    然后看每层遍历多少个元素,首先最高层最多遍历3个节点,就能往下走了,同理,次高层也最多遍历三个节点,就能往下走。取平均之后,可以认为每层遍历2个节点。

    因此时间复杂度=2log2(n),同理,如果是每k个节点取一个索引的话,就是klogk(n)

    空间复杂度:也是以每两个节点取一个索引为例,第一层n个节点,第二层n/2,第三次n/4,等比序列求和,或者取极限,可以认为索引节点数量无限接近于n,所以空间复杂度为O(n)。

  • 相关阅读:
    人大女王大学金融硕士项目——披星戴月走过的路,一定可以繁花满地
    SAP UI5 SimpleForm 的 two-column layout 布局
    推特因裁员被集体诉讼?马斯克称“别无选择”,已给予赔偿
    第8章 docker网络管理
    【云原生 | Kubernetes篇】自建高可用k8s集群优化
    GitHub最新发布,阿里十年架构师手写版spring全家桶笔记全新开源
    JSP Webshell 免杀
    人力资源APP功能
    css 雷达扫描图
    网页解析器 —— beautiful soup
  • 原文地址:https://blog.csdn.net/qq_52563729/article/details/126030846