redis底层数据结构

Redis是互联网技术领域使用广泛的存储中间件,其全称是“Remote Dictionary service”。Redis以其高性能、丰富的文档、简洁易懂的源码和丰富的客户端支持受到好评,许多大型公司都在使用,比如新浪微博、twitter、阿里巴巴、华为、腾讯等。

1redis的常见数据类型

string(字符串):内部是一个带长度信息的字符数组,也叫“SDS”。Redis的字符串是动态字符串,可以修改,类似于StringBuilder类,单个Redis的键和值大小不能超过512MB。常见的string操作指令就不多说了,大家都明白。

struct SDS<T>{
    //数组容量
    T capactity;
    //数组长度
    T len;
    //数组标志位
    byte flags;
    //数组内容
    byte[] content;
}

Redis的字符串有两种存储方式,当长度较短时,使用embstr存储,当长度超过44字节时,使用raw存储,如下图所示。

为什么会有这种现象,要先了解redis的对象头结构,所有的redis对象都有下面的对象头结构

struct RedisObject{
    int4 type;    4bits
    int4 encoding;  4bits
    int24 lru;  24bits
    int32 refcount;  4bytes;
    void *ptr;  8bytes
}

查看之前的SDS结构体,当字符串比较小时,分配一个字符串的最想空间为(16+3)字节,在分配内存时,大小是按2/4/8/16/32/64字节来分配的,为了能完整容纳一个embar对象,最少会分配32字节,再长就是64字节,而SDS中字符串是以NULL结尾的,所以留给字节数组的大小为64-1-19=44字节,再大就会转为raw类型,这就是原因。

list(列表):redis中的list相当于Java中的LinkedList,这意味着既可以用来当作栈,也可以用来当作队列,具有较快的插入和删除性能。实际上,list是一个quickList结构,称为压缩列表,他将所有的元素彼此紧挨着一起存储,分配的是一块连续的内存,有兴趣的可以阅读源码进行学习,我也正在阅读,希望有所收获。list的用途很广泛,可以用来获取最新的数据,比如获取对某条信息的最新评论,存储是只要按照队列来使用能完成功能。

struct quicklistNode{
    quicklistNode* prev;
    quicklistNode* next;
    ziplist* zl;
    int32 size;
    int16 count;
    int2 encoding;
}

struct quicklist{
    quicklistNode* head;
    quicklistNode* tail;
    long count;
    int nodes;
    int compressDepth;
}  

hash(字典):redis中的hash相当于Java中的HashMap,内部存储了很多键值对,我们可以使用对应的命令来修改hash中对应的键值对的值。redis中的hash的值只能是string类型,并且由于redis追求的是高性能,所以采用了渐进式rehash策略,渐进式rehash策略会在rehash的同时,保留新旧两个hash结构,查询是会同时查询新旧两个hash结构,然后在后续的定时任务和使用中,将旧hash中的值慢慢迁入到新hash中,当搬迁完成,旧hash就会被替换删除了。

//渐进式hash
dictEntry *dictAddRaw(dict *d,void *key,dictEntry ** existing){
    long index;
    dictEntry *entry;
    dictht *ht;
    
    //这里进行小步搬迁
    if(dictIsRehashing(d)) _dictRehashStep(d);
        if((index=_dictKeyIndex(d,key,dictHashKey(d,key),existing))==-1){
            return null;
        }
    
    //如果字典处于搬迁过程中,需要将新的元素挂到新的数组下面
    ht=dictIsRehashing(d)?&d->ht[1]:&d->ht[0];
    entry=zmalloc(sizeof(*entry));
    entry->next=ht->table[index];
    ht->table[index]=entry;
    ht->used++;

    dictSetKey(d,entry,key);
    return entry;
}

func get(key){
    let index=hash_func(key)%size;
    let entry=table[index];
    while(entry!=null){
        if(entry.key==target){
            return entry.value;
        }
    }
}

set(集合):redis的集合相当于Java的HashSet,他的内部实现当于一个字典,内部的键值对是无序的,并且每个键值对的值都为null,当删除最后一个元素时,数据结构被自动删除,可以用来存储中奖的信息,具有去重功能。

zset(有序集合):这可以说是redis使用最多的数据结构,可以用来记录热榜,分数排名,记录帖子的点赞用户排名,在实际运用的非常广泛。他在set的基础上维护了一个score,可以是数目,时间,代表了这个value的权重,通过zrange和zrevrange两个命令来顺序遍历和倒序遍历zset,他的内部实现是跳跃列表结构。

struct zslnode{
    string value;
    double score;
    zslnode*[] forwards;
    zslnode*[] backword;
}

struct zsl{
    //跳跃列表头指针
    zslnode* header;
    //跳跃列表最高层
    int maxLevl;
    //所有的键值对
    map<String,zslnode*> ht;
}

关于跳跃列表,可参考:https://blog.csdn.net/lz710117239/article/details/78408919

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值