Redis-数据结构05-字典(dict)

Redis字典这个数据结构,最重要的地方就是用于主数据库的K-V数据存储

redis中的字典与java中的HashMap一模一样,不过java8之后链表变成了红黑树,而redis中并没有这样做,依然是个链表

redis的字典结构中,key可以是字符串,整数,浮点,但无论redis客户端set一个什么类型的key(字符串,整数,浮点),最终到redis服务端的时候,都是字符串的形式,也就是说,key的类型都是字符串,不可能有其他类型,而value可以是String,Hash,List,Set,SortedSet

Redis自带客户端就是使用times 33散列函数来计算字符串的Hash值,Redis服务端的Hash函数使用的是siphash算法,主要功能与客户端Hash函数类似,其优点是针对有规律的键计算出来的Hash值也具有强随机分布性,但算法较为复杂

查找key:先hash一下key,然后找到位置,如果该位置存在多个元素,那么就轮询这多个元素,对比key,如果key相等,就找到了该元素

下面是字典的结构体代码,字典中包含多个hash表

typedef struct dict {
    
    
	//该字典对应的特定操作函数,文末有介绍
    dictType *type;           
    //该字典依赖的数据
    void *privdata;          
    //Hash表,键值对存储在此
    dictht ht[2];    
    //rehash标识。默认值为-1,代表没进行rehash操作;不为-1时,代表正进行rehash操作,存储的值表示Hash表ht[0]的rehash操作进行到了哪个索引值          
    long rehashidx;           
    //当前运行的迭代器数
    unsigned long iterators; 
} dict;

下面是客户端Hash表结构体代码,Hash表中包含多个dictEntry(键值对)

typedef struct dictht {
    
    
	//指针数组,用于存储键值对,注意这里是2个*
    dictEntry **table;     
    //table数组的大小
    unsigned long size;    
    //恒定=size-1
    unsigned long sizemask;
    //table数组已存元素个数,包含next单链表的数据
    unsigned long used;    
} dictht;

注意sizemask:初始的时候认为设定Hash表的数组容量初始值为4,随着键值对存储量的增加,就需对Hash表扩容,新扩容的容量大小设定为当前容量大小的一倍,也就是说,Hash表的容量大小只能为4,8,16,32…。而sizemask掩码的值就只能为3,7,15,31…,对应的二进制为11,111,1111,11111…,因此掩码值的二进制肯定是每一位都为1。
如果没有sizemask,假设hashCode的大小是10,容量是4,那么获取hash的方式就是10除以4余2,所以hash=2,
现在有sizemask了,假设hashCode的大小是10,容量是4,sizemask则等于3,然后将hashCode和sizemask进行与操作,也就是10&3=2,所以hash=2
以上两种方式结果相同,但是计算机中,与运算要比求余快很多,redis这个算法还是很巧妙的

下面是dictEntry结构体

typedef struct dictEntry {
    
    
    void *key;                        /*存储键*/
    union {
    
    
        void *val;                        /*db.dict中的val*/
        uint64_t u64;
        int64_t s64;                /*db.expires中存储过期时间*/
        double d;
    } v;                                /*值,是个联合体*/
    struct dictEntry *next;        /*当Hash冲突时,指向冲突的元素,形成单链表*/
} dictEntry;

Hash表中元素结构体和我们前面自定义的元素结构体类似,整体占用24字节,key字段存储的是键值对中的键。v字段是个联合体,存储的是键值对中的值,在不同场景下使用不同字段。例如,用字典存储整个Redis数据库所有的键值对时,用的是*val字段,可以指向不同类型的值;再比如,字典被用作记录键的过期时间时,用的是s64字段存储;当出现了Hash冲突时,next字段用来指向冲突的元素,通过头插法,形成单链表。

下面是dictType结构体

typedef struct dictType {
    
    
	//该字典对应的Hash函数
    uint64_t (*hashFunction)(const void *key);     
    //键对应的复制函数           
    void *(*keyDup)(void *privdata, const void *key);    
    //值对应的复制函数     
    void *(*valDup)(void *privdata, const void *obj); 
    //键的比对函数        
    int (*keyCompare)(void *privdata, const void *key1, const void *key2); 
    //键的销毁函数
    void (*keyDestructor)(void *privdata, void *key);  
    //值的销毁函数   
    void (*valDestructor)(void *privdata, void *obj);     
} dictType;

Redis字典这个数据结构,除了主数据库的K-V数据存储外,还有很多其他地方会用到。例如,Redis的哨兵模式,就用字典存储管理所有的Master节点及Slave节点;再如,数据库中键值对的值为Hash类型时,存储这个Hash类型的值也是用的字典。在不同的应用中,字典中的键值对形态都可能不同,而dictType结构体,则是为了实现各种形态的字典而抽象出来的一组操作函数。

日记:
1.Redis字典这个数据结构,最重要的地方就是用于主数据库的K-V数据存储
2.巧妙的sizemask算法


猜你喜欢

转载自blog.csdn.net/u011624903/article/details/109713158