Redis:数据结构与对象

一、简单动态字符串SDS

struct sdshdr{
    //记录buf数组中已使用的字节的数量,等于SDS所保存的字符串长度
    int len;
    //记录buf数组中未使用字节的数量
    int free;
    //字节数组,用于保存字符串
    char buf[];
}

SDS与C字符串的区别:

  • 常数复杂度获取字符串长度
  • 杜绝缓冲区溢出
  • 减少修改字符串时带来的内存重分配次数

二、双端链表

Redis列表键的实现之一

typedef struct listNode{

    struct  listNode * prev;
    struct  listNode * next;
    void *value;
}

多个listNode可以通过prev和next指针组成双端链表

虽然仅仅使用多个 listNode 结构就可以组成链表,但使用 list 来持有链表的话,操作起来更方便

typedef struct list{
    //表头结点
    listNode * head;
    //表尾节点
    listNode * tail;
    //表长度
    unsigned long len;
    //节点复制函数
    void *(*dup)(void *ptr);
    //节点释放函数
    void (*free) (void *ptr);
    //节点对比函数
    int (*match) (void *ptr,void *key);
}

Redis链表实现的特性如下:

  • 双端
  • 无环
  • 带表头指针和表尾指针
  • 带链表长度计数器
  • 多态:链表节点使用void* 指针来保存节点值,并且使用list结构的三个而函数未节点值设置类型特点函数,所以可以保存不同类型的值

三、字典

字典是一种用于保存键值对的抽象数据结构,字典中的每个键都是独一无二的,程序在字典中根绝键查找与之关联的值,或者通过键来更新值,有或者根据键来删除键值对。Redis构建了自己的字典实现。

Redis数据库就是使用字典作为底层实现的。

SET msg "hello word"

在数据库中创建一个键位”msg“,值为"hello world"的键值对时,这个键值对就是保存在代表数据库的字典里的。

字典还是哈希键(使用hset key field value时)的底层实现之一,当一个哈希键包含的键值对比较多,或者键值对中的元素都是比较长的字符串时,Redis就会使用字典作为哈希键的底层实现

3.1 字典的实现

字典使用哈希表作为底层实现,一个哈希表里多个哈希表节点,每个节点保存字典中的一个键值对。

哈希表定义:

typedef struct dictht{
    //哈希表数组
    dictEntry **table;
    //哈希表大小
    unsigned long size;
    //哈希表大小掩码
    unsigned long sizemask;
    //哈希表已有结点的数量
    unsigned long used;

}

哈希节点定义:

typedef struct dicEntry{
    //键
    void *key;
    //值
    union{
        void *val;
        uint64_tu64;
        int64_ts64;    
    }v;
    //指向下一个哈希表节点,哈希冲突时连接在一起
    struct dictEntry *next;
}dictEntry;

字典定义:

typedef struct dict{
    dictType *type;
    void *privdata;
    //哈希表
    dicht ht[2];
    //rehash索引,扩展和收缩哈希表时用到
    in trehashidx;
}dict

四、跳跃表

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

Redis两个地方用到跳跃表:

  • 有序集合键
  • 集群节点

跳跃表实现,由两个结构定义:

  • zskiplistNode(跳跃表节点)
  • zskiplist(保存跳跃表节点相关信息)

4.1 zskiplist

  • header:指向跳跃表的表头节点
  • tail:指向跳跃表的表尾节点
  • level:记录目前跳跃表内,层数最大的那个节点的层数(表头节点的层数不计算)
  • length:跳跃表的长度,即目前包含节点的数量(表头节点不计算)
typedef struct zskiplist{
    struct zskiplistNode *header,*tail;
    unsigned long length;
    int level;
}zskiplist;

4.2 zskiplistNode

typedef struct zskiplistNode{
    struct zskiplistlevel{
        struct zskiplistNode * forward;
        unsigned int span;
    }level[];
    struct zskiplistNode *backward;
    double score;
    robj *obj
}zskiplistNode;

level:
level数组包含多个元素,每个元素都包含一个指向其他zskiplistNode节点的指针,一般来说层的数量越多,访问其他节点的速度越快,每次创建一个新的跳跃表节点的时候,随机生成一个介于1-32之间的值N,作为level数组的大小(越大的数出现概率越小),这个大小就是层的高度,从第1层连续创建到第N层。

前进指针(forward):
每个层都有一个指向表尾方向的指针

跨度(span):层的跨度用于记录两个节点的举例,指向NULL时跨度为0。跨度与遍历无关,用来计算排位(rank):查找某个节点的过程中,将沿途访问的所有层的跨度累加,就是目标节点在跳跃表的排位

后退指针(backward):
用于表尾方向向表头方向访问节点,每个节点只有一个后退指针,只能后退至前一个节点

分值和成员(score和obj):
score是double类型的浮点数,跳跃表中的所有节点都按分值从小到大来排序,不同节点score可以相同(从小到大排)。obj是一个指针,指向字符串对象,字符串对象保存SDS值,各节点的obj唯一

五、整数集合(inset)

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

typedef struct intset{
    //编码方式
    uint32_t encoding;
    //集合包含元素数量
    uint32_t length;
    //保存元素的数组,有序无重复
    int8_t contents[];
}inset;

在有需要时,程序会根据新添加元素的类型,改变这个数组的类型。

六、压缩列表

压缩列表(ziplist)是列表键(LIST)和哈希键(HASH)的底层实现之一,当一个列表键只包含少了列表项,并且每个列表项妖媚就是小整数值,要么就是长度比较短的字符串,那么Redis就会使用压缩列表作为底层实现。

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

七、对象

上面介绍了六种数据结构,但Redis并没有直接使用这些数据结构来实现键值对数据库,而是基于数据结构创建一个对象系统。包括:

  • 字符串对象(整数值/长度大于32->SDS/长度小于32->embstr):string
  • 列表对象(压缩列表/双端链表):list
  • 哈希对象(压缩列表/字典):hash
  • 集合对象(整数集合/字典):set
  • 有序集合对象(压缩列表/跳跃表):zset

通过五种不同类型的对象,Redis可以在指向命令前,根据对象类型和使用场景来为对象设置多种不同的数据结构实现。

对于Redis数据库保存的键值对来说,键总是一个字符串对象,而值则可以时上面的五种对象,

  • 当称呼一个数据库键位”字符串键“时,指的是数据库键所对应的值为字符串对象
  • 当称呼一个数据库键位”列表键“时,指的是数据库键所对应的值为列表对象

7.1 zset

zet中的两种数据结构:

  • 字典:从成员到分值的映射,字典的键保存元素成员,字典的值是分值
  • 跳跃表:按分值从小到大保存所有集合元素,每个结点保存了成员和分值

这两种数据结构会共享相同元素的成员和分值

zscore key member

作用:指定成员的分数

原理:从字典中取出成员分值

zrange key start stop

作用:查询有序集合,指定区间的内的元素。start和stop是元素索引

原理:根据span查找起始位置,从高层到底层,利用每一层节点的span跨度去累加判断,找到起始位置,如果不等于(超过起始位置)则按当前节点位置降低层数继续查找

zrank key member

作用:返回有序集合中指定成员的索引

原理:根据成员从字典查到分数之后,去跳表查找对应的元素,根据跨度计算排名,时间复杂度为O(log n)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值