Redis存储原理与数据模型

字典数据结构实现

redis DBKV 是通过字典来实现的;hash结构,当节点数量大于512个,或单个字符串长度大于64,hash结构采用字典实现;

相关数据结构如下

typedef struct dictEntry {
    void *key;
    union {
        void *val;
        uint64_t u64;
        int64_t s64;
        double d;
    } v;
    struct dictEntry *next;
} dictEntry;

/* This is our hash table structure. Every dictionary has two of this as we
 * implement incremental rehashing, for the old to the new table. */
typedef struct dictht {
    dictEntry **table;
    unsigned long size; //数组长度
    unsigned long sizemask;//size - 1
    unsigned long used; //当前数组中包含的元素
} dictht;

typedef struct dict {
    dictType *type;
    void *privdata;
    dictht ht[2];
    long rehashidx; /* rehashing not in progress if rehashidx == -1 */
    int16_t pauserehash; /* If >0 rehashing is paused (<0 indicates coding error) */
} dict;

1.字符串经过hash运算得到64位整数;

2.相同字符串多次通过hash函数得到相同的64位整数;

3.整数对2的n次幂取余可以转化位运算

4.抽屉原理,n+1个苹果放到n个抽屉里,至少有一个抽屉的苹果个数大于等于2;64位整数远大于数组的长度,比如数组的长度位4,那么1 ,5,9,1+4n都映射到1号位数组,所以大概率会发生冲突

 冲突

负载因子=used/size;used是数组中存储的元素的个数,size为数组大小,负载因子越大,发生冲突的可能性就越大,负载因子越小,发生冲突的可能性就越小。

redis的负载因子为1

扩容,当负载因子大于1后,就会发生扩容,扩容的规则是翻倍

但是,如果redis正在fork(在rdb,aof复写以及rdb-aof混用的情况)时,会组织扩容,但若此时负载因子大于5,索引效率极具下降,则立即扩容。

 扩容是,相应插槽位置的数据会扩容到本来以及与其对应的扩容位置上,如下图

redis扩容时采取写时复制的原则,写时复制的核心思想是,只有在不得不复制的时候复制数据内容,示意图如下

 缩容

如果负载因子小于0.1,则会发生缩容;缩容的规则是恰好包含used的2的n次方;

恰好的理解为:如果此时数组存储的元素个数为9,恰好包含这些元素的2的次方为2的4次方,也就是16个位置。缩容的规则如下图示意

渐进式rehash

当hashtable中元素过多的时候,不能一次性rehash到ht[1],这样会长期占用redis,其他命令得不到响应,所以需要使用渐进式rehash

rehash步骤

将ht[0]中的元素重新经过hash函数生成64位整数,再对ht[1]取余,从而映射到ht[1]

渐进式规则:

1.分治思想,将rehash分到之后的每步增删改查的操作之中;

2.在定时器中,最大执行一毫秒;每次步长100个槽位

注:处于渐进式rehash中会不会扩容,答案是不会的 

大KEY

在redis实例中形成了很大的对象,比如一个很大的hash或很大的zset,这样的对象在扩容时,会一次性申请很大的内存,这会导致卡顿;如果这样的大key被删除,内存会一次性回收,卡顿现象会再次产生

如果观察到redis的内存大起大落,极有可能是大KEY导致的

该命令可以查询大key

跳表实现

 理想跳表

 

 redis跳表

从节约内存的角度出发,redis考虑牺牲到一点时间复杂度让跳表结构变的更加扁平,就像二叉堆改成四叉堆那样,并且redis限制跳表的最高层级位32

在redis中,节点数量大于128或字符串长度大于64,采用跳表结构(skiplist)

#define ZSKIPLIST_MAXLEVEL 32 /* Should be enough for 2^64 elements */
#define ZSKIPLIST_P 0.25      /* Skiplist P = 1/4 */

/* ZSETs use a specialized version of Skiplists */
typedef struct zskiplistNode {
    sds ele;
    double score;
    struct zskiplistNode *backward;
    struct zskiplistLevel {
        struct zskiplistNode *forward;
        unsigned long span;
    } level[];
} zskiplistNode;

typedef struct zskiplist {
    struct zskiplistNode *header, *tail;
    unsigned long length;
    int level;
} zskiplist;

typedef struct zset {
    dict *dict;
    zskiplist *zsl;
} zset;

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值