zoukankan      html  css  js  c++  java
  • Redis设计与实现--数据结构与对象

    1 简单动态字符串--simple dynamic string

    实现

    相对于C字符串

    1. 常数复杂度获取字符串长度

    2. 杜绝缓冲区溢出

    3. 减少修改字符串时带来的内存重分配次数(空间预分配,惰性空间分配)

    4. 二进制安全(不仅可以保存文本数据,还可以保存任意格式的二进制数据)

    5. 兼容部分C字符串函数

    2 链表

    实现

    list  listNode

    特点

    双端, 无环, 带表头指针和表尾指针, 带链表长度计数器, 多态

    3 字典

    用处

    Redis数据库, 哈希键的底层实现

    数据结构

    哈希表dictht  哈希表节点dictEntry  字典dict

    哈希算法

    MurmurHash2算法

    键冲突

    将新节点添加到链表的表头位置

    rehash过程

    1) 为ht[1]分配空间

    2) rehashindex = 0;

    3) 每次CURD时,将ht[0]在reindexhash索引上的所有键值对rehash到ht[1]中, rehashindex++;

    4) 待所有键值对都被rehash到ht[1]中, rehashindex = -1.

    4 跳跃表--skiplist

    时间复杂度

    最好O(logN),最坏O(N)

    用处

    有序集合键的底层实现之一(有序结合中元素比较多,或者有序集合中成员是比较长的字符串时), 集群节点中用作内部数据结构

    数据结构

    zskiplistNode的结构: 

    typedef struct zskiplistNode {
        //
        struct zskiplistLevel {
            //前进指针
            struct zskiplistNode *forward;
    
            //跨度
           unsigned int span;
        } level[];
    
        //后退指针
        struct zskiplistNode *backward;
    
        //分值
        double score;
    
        //成员对象
        robj *obj;
    } zskiplistNode;
    typedef struct zskiplist {
        //表头节点和表尾节点
        struct zskiplistNode *header, *tail;
    
        //表中节点的数量
        unsigned long length;
    
        //表中层数最大的节点的层数
        int level;
    } zskiplist;

    5 整数集合--intset

    用处

    一个集合中只包含整数值元素,并且这个集合的元素数量不多时

    复杂度

    向整数集合添加新元素的时间复杂度为O(N)

    升级的好处

    提高灵活性,节约内存

    注意

    intset不支持降级

    6 压缩列表--ziplist

    用处

    列表键(只包含少量列表项,并且列表项要么就是小整数值,要么就是长度比较短的字符串)和哈希键(只包含少量键值对,并且每一个键值对的键和值要么就是小整数值,要么就是长度比较短的字符串)的底层实现之一

    实现

    其中,previous_entry_length属性长度为一字节,五字节,记录了前一个节点的长度,所有程序可以通过指针运算,根据当前节点的起始地址来计算前一个节点的起始地址.

    encoding属性记录了content属性所保存数据的类型及长度,一字节,二字节,五字节,值的最高位为00,01,10,表示保存字节数组,其他位表示字节数组的长度;一字节的最高位为11,表示整数,类型和长度由其他位确定.

    连锁更新

    添加新节点,删除节点都可能触发连锁更新

    连锁更新的最坏时间复杂度为O(N²),但ziplistPush等命令的平均时间复杂度为O(N).

    7 对象

    Redis基于以上的数据结构创建了一个对象体系,包含了字符串对象,列表对象,哈希对象,集合对象,有序集合对象这五种对象. 

    Redis的对象体系还实现了基于引用计数技术的内存回收机制,同时基于引用计数技术实现了对象共享机制,在适当条件,通过多个数据库键共享同一个对象来节约内存.

    Redis中的每一个对象都由一个redisObject结构表示:

     1 typedef struct redisObject {
     2     //类型
     3     unsigned type:4;
     4     
     5     //编码
     6     unsigned encoding:4;
     7 
     8     //指向底层实现数据结构的指针
     9     void *ptr;
    10 
    11     // ...
    12 } robj;

    refcount表示引用计数;

    lru记录了对象最后一次被应用程序使用的时间,空转时间为当前时间减去lru的值,如果服务器打开了maxmemory选项,并且回收内存算法为volatile-lru,allkeys-lru,那么当内存占用数超过maxmemory的上限,那么空转时间较高的那部分键将会被优先释放;

    type表示对象的类型,键总是一个字符串对象,值可以是五种对象之一;

    encoding记录了对象所使用的编码,

    7.1 字符串对象

    7.1.1 类型与编码

    如果保存的是整数值,且可用long类型表示,那么编码设为int;

    如果保存的是一个字符串,并且长度大于32字节,那么使用SDS保存,编码设为raw;

    如果保存的是一个字符串,并且长度小于或等于32字节,那么编码设为embstr;

    7.1.2 保存的具体值与编码

    可以用long类型保存的整数,编码为int;

    可以用long double类型保存的浮点数,编码为embstr或raw;

    字符串值,或者长度太长无法用long类型保存的整数,或者长度太长无法用long double类型保存的浮点数,编码为embstr或raw.

    7.1.3 转换

    Redis中embstr编码的字符串对象是只读的,对之的任何修改,都将使之转换为raw编码的字符串对象.

    7.2 列表对象

    ziplist编码的列表对象使用压缩列表作为底层实现,

    linkedlist编码的列表对象使用双端列表作为底层实现.

    7.2.1 编码转换

    使用ziplist需满足:

    • 所有字符串长度小于64字节;此值可改:list-max-ziplist-value
    • 元素数量小于512个;此值可改:list-max-ziplist-entries

    7.3 哈希对象

    ziplist编码的哈希对象使用压缩列表作为底层实现,

    hashtable编码的哈希对象使用字典作为底层实现;其中字典的每一个键值都是一个字符串对象. 

    7.3.1 编码转换

    使用ziplist需满足:

    • 所有键值对的字符串长度都小于64字节;此值可改hash-max-ziplist-value
    • 保存的键值对数量小于512个;此值可改:hash-max-ziplist-entries

    7.4 集合对象

    intset使用整数集合作为底层实现;

    hashtable使用字典作为底层实现;

    7.4.1 编码转换

    使用intset需满足:

    • 集合对象所包含的所有元素都是整数;
    • 集合对象所包含的元素数量不超过512个;此值可改:set-max-ziplist-entries

    7.5 有序集合对象

    7.5.1 ziplist编码的有序结合对象

    对象使用压缩列表作为底层实现,每个集合元素使用两个挨在一起的压缩列表节点来保存,第一个节点保存元素的成员,第二个节点保存元素的分值.

    7.5.2 skiplist编码的有序结合对象

    skiplist对象使用zset结构作为底层实现,之中同时包含一个字典和跳跃表;

    1 typedef struct zset {
    2     zskiplist *zsl;
    3     dict *dict;   
    4 } zset;

    zsl跳跃表按分值从小到大保存了所有集合元素,通过跳跃表,可以对有序结合进行范围操作,比如zrank,zrange;

    dict字典保存了从成员到分值的映射,可以用O(1)复杂度查询给定成员的分值,比如zscore.

    注意:zsl和dict会通过指针来共享相同元素的成员和分值,不会产生任何重复成员或分值,不会因此产生额外的内存.

    7.5.3 编码的转换

    使用ziplist需满足:

    • 有序集合保存的元素数量小于128个;此值可改:zset-max-ziplist-entries
    • 有序集合保存的所有成员的长度都小于64位;此值可改:zset-max-ziplist-value
  • 相关阅读:
    代码整洁之道 读书笔记
    AJAX分页带页码
    下拉框绑定数据
    Excel导入导出
    万能分页存储过程
    android 更新uI主线程
    eclipse配置j2ee项目
    java常见错误云集与注意事项
    亚马逊服务器搭建
    常见sql的error解决方法
  • 原文地址:https://www.cnblogs.com/cheungchein/p/8664034.html
Copyright © 2011-2022 走看看