首页 > 数据库 >redis底层数据结构之字典(dict)

redis底层数据结构之字典(dict)

时间:2022-12-17 18:13:08浏览次数:41  
标签:rehash 哈希 void redis ht dict key 数据结构

字典(dict)

字典又称为符号表或者关联数组、或映射(map),是一种用于保存键值对(key-value)的抽象数据结构

字典中的每个key都是唯一的,通过key对值来进行查找或修改,时间复杂度为 O(1)

redis的底层数据结构字典又使用了哈希表,一个哈希表包含多个哈希表节点,每个哈希表节点保存了字典中的一个键值对

 

1 字典结构

typedef struct dict {
    dictType *type;
    void *privdata;
    dictht ht[2];
    int rehashidx; 
} dict;

其中:

type:类型特定函数,保存了一系列操作dict的函数, redis会为用途不同的字典设置不同的类型特定函数

privdata:私有数据,需要传给type属性中定义的类型特定函数的可选参数

dictht:哈希表,包含两个元素,每个元素都是一个哈希表,一般情况下,字典只使用 ht[0]哈希表,ht[1]哈希表只会在对 ht[0] 哈希表进行 rehash 时使用

rehashidx:rehash索引,不在进行rehash时,值为 -1

 

2 dictType(类型特定函数)结构

typedef struct dictType {
    unsigned int (*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;

其中:

hashFunction:计算哈希值的函数

keyDup:复制键的函数

valDup:复制值的函数

keyCompare:对比键的函数

keyDestructor:销毁键的函数

valDestructor:销毁值的函数

 

3 dictht(哈希表)结构

typedef struct dictht {
    dictEntry **table;
    unsigned long size;
    unsigned long sizemask;
    unsigned long used;
} dictht;

其中:

table:哈希表数组,每一个哈希表数组元素都保存了一对键值对

size:哈希表的大小

sizemask:哈希表掩码,值等于 size-1,用于通过hash算法计算索引值

used:哈希表已使用节点的数量

 

4 dictEntry(哈希表节点)结构

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

 

其中:

key:dict中的key

v:dict的value,可以是一个指针,也可以是uint64_t 整数,也可以是int64_t整数

next:指向下一个哈希表节点的指针,当有hash冲突时,解决hash冲突使用,redis使用链地址法解决哈希冲突

 

5 字典示意图

存有4个key-value对且没有进行rehash的字典结构如下:

type=REDIS_SET 或 REDIS_HASH

 

 

6 hash算法

dict要添加新的key-value对时, redis使用hash算法计算索引值, 根据索引值将key-value对放到哈希表数组的对应位置

redis中计算索引值步骤如下:

 

1) 计算key的哈希值

使用dict结构的type属性中定义的类型特定函数,计算键(key)的哈希值

redis 使用 MurmurHash2 算法来计算键的哈希值

hash = dict->type->hashFunction(key);

 

2) 计算索引值

使用哈希表的sizemake属性和哈希值,计算出索引值,根据情况不同:ht[x]可以是 ht[0]或者 ht[1]

index = hash & dict->ht[x].sizemask;

 

7 hash冲突

哈希表会存在哈希冲突,解决哈希冲突的方法有:开放地址法和链地址法

redis采用的是链地址法,通过next指针可以将多个哈希值相同的键值对连接在一起,用来解决哈希冲突

rehash会增加现有的哈希桶数量,让逐渐增大的entry元素能在更多的桶之间分散保存,减少单个桶中的元素数量,从而减少单个桶中的冲突

 

8 扩容和收缩(rehash)

当哈希表保存的key-value对太多或太少时,就要通过rehash(重新散列)对哈希表进行扩展或者收缩

dict中存有的两张哈希表:

ht[0]哈希表表示字典正在使用的哈希表,key-value对存在ht[0]中

ht[1]哈希表表示rehash时使用的哈希表,没有申请结点内存空间,只有表结构,不会占用很大的内存空间

步骤如下:

1) 为ht[1]重新分配空间,分配的空间大小取决于要执行的操作,以及ht[0]当前包含的key-value对数量(即ht[0].used的值)

  a 如果执行的是扩展操作,扩展为原哈希表已使用的空间的2倍

  b 如果执行的是收缩操作,收缩为原哈希表已使用空间的一半

2) 使用hash算法重新计算键的哈希值和索引,然后将key-value对存放到ht[1]的对应位置上
3) 当ht[0]包含的所有key-value对都迁移到ht[1]之后(ht[0]变为空),释放ht[0],将ht[1]设置为ht[0],rehashidx设置为 -1

 

9 渐近式rehash

扩容和收缩操作不是一次性、集中式完成的,而是分多次、渐进式完成的

如果哈希表里保存的key-vlaue对的数量在百万级别,那么一次性将所有key-vlaue对rehash,可能会造成redis在一段时间内不能进行别的操作,所以redis采用渐进式rehash,分多次、渐进式完成地将ht[0]里面的key-vlaue对慢慢地rehash到ht[1]

dict中索引计数器变量rehashidx=0表示开始rehash,每次处理请求时将ht[0]索引位置(rehashidx=上一个rehashidx+1)的所有key-value对拷贝到ht[1],完成拷贝时,rehashidx+1;

渐进式rehash期间,字典的删除、查找、更新等可能会在两个哈希表上进行(不是同时进行),第一个哈希表没有找到,就会去第二个哈希表上进行查找;但是插入key-value对一定是在新的哈希表上进行

 

 

10 扩容和收缩的条件

负载因子 = 哈希表已保存节点数量 / 哈希表大小

 

1) 扩容操作

a 服务器目前没有在执行bgsave命令或者bgrewriteaof命令并且哈希表的负载因子大于等于1

b 服务器目前正在执行bgsave命令或者bgrewriteaof命令并且哈希表的负载因子大于等于5

 

2) 收缩操作

a 哈希表的负载因子小于 0.1 ,程序自动开始对哈希表执行收缩操作

 

标签:rehash,哈希,void,redis,ht,dict,key,数据结构
From: https://www.cnblogs.com/junffzhou/p/16972094.html

相关文章

  • redis底层数据结构之跳表(skiplist)
    跳表(跳跃表,skiplist)跳跃表(skiplist)是用于有序元素序列快速搜索查找的数据结构,跳表是一个随机化的数据结构,实质是一种可以进行二分查找的、具有层次结构的有序链表......
  • redis底层数据结构之整数集合(intset)
    整数集合(intset)当一个集合只包含整数值元素,并且这个集合的元素数量不多时,redis会使用整数集合(intset)作为集合键的底层实现整数集合用于保存整数值的集合抽象数据类型......
  • redis之五种基本数据类型
    五种基本数据类型redis存储任何类型的数据都是以key-value形式保存,并且所有的key都是字符串,所以讨论基础数据结构都是基于value的数据类型常见的5种数据类型是:String、Li......
  • redis底层数据结构之简单动态字符串(SDS)
    简单动态字符串(simpledynamicstring,SDS)redis使用C语言编写的,但是redis的字符串却不是C语言中的字符串(以空字符'\0'结尾的字符数组),redis定义了一种简单动态字符串(s......
  • redis底层数据结构之双向链表(linkedlist)
    双向链表(linkedlist)redis的双向链表(linkedlist)是基于链表的一种数据结构链表是一种常见的基础数据结构,是一种非顺序存储数据的线性表,在每一个节点里存储了下一个节点......
  • redis底层数据结构之压缩列表(ziplist)
    压缩列表(ziplist)压缩列表(ziplist)是redis为了节约内存而开发的,由连续内存块组成的顺序型数据结构,适用于长度较小的值存取的效率高,内存占用小,但由于内存是连续的,在修......
  • redis底层数据结构之快速列表(quicklist)
    快速列表(quicklist)redis3.2版本之前,List类型数据使用的底层数据结构是压缩列表(ziplist)或双向链表(linkedlist),当列表元素个数比较少并且每个元素占用空间比较小时使......
  • redis常用命令之string&list
    redis常用操做stringkey操作string<key:value>setnamejohngetnamelistsetnx<keyvalue>setnxgendermale(分布式锁)getgendersetnxgoods_1111delgoods_1ge......
  • 数据结构和算法day1(Java)
    1.什么是数据结构?数据结构就是把数据元素按照一定的关系组织起来的集合,用来组织和存储数据。1.2.数据结构的分类:逻辑结构和物理结构逻辑结构:集合结构(无关系)、线性结......
  • redis分布式锁
    redis分布式锁C#集合线程问题:https://www.cnblogs.com/Clingingboy/archive/2010/12/06/1897534.htmlC#多线程安全集合类:https://www.cnblogs.com/Darius0821/p/16967......