reids (2)链表与字典

redis 底层数据结构之链表:
list:{

listnode *head

listnode *tail

unsigned long len 所包含节点数量

void *(*dup) (void *ptr); 复制当前节点所保存的值

void (*free)(void *ptr ) 释放节点所占用内存

int (*match)(void *ptr,void *key) 节点值对比函数

}

listnode:{

listnode  *prev

listnode *next

void *value

}listnode

特别说明 head 节点的prev指针与tail节点的next指针是一个空值,也就意味着,该链表是无环链表

 

字典:

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

redis的字典是使用哈希表来实现的

而哈希表的结构用dictht定义表示

dictht{

// **table 为哈希表数组

 dictentry **table

//table的大小

unsigned long size

// 哈希表大小掩码,用以计算哈希值,总等于size-1

unsigned long_sizemask

// 哈希表已有节点数量

unsigned long used

}

table是一个数组,数组中的每一个元素都是指向dictentry的指针,每个dictentry保存着一个键值对

dictentry{

void *key

union {

void *val;

uint64_t u64;

int 64_t s64;

}v;

//指向下一个dictentry指针

struct dictentry *next  (next 属性指向另一个哈希表节点的指针,这个指针可以将多个哈希值相同的键key连接在一起),形成一个链表

 

通过对上述两种数据结构的介绍,可以得到redis字典的结构:

dict:{

 

dicttype *type;

void *privdata;//私有数据

dictht ht[2] // 哈希表

int trehashidx //当rehash不在进行时,值为-1

}dict

 

重点介绍type属性,type属性是一个指向dicttype结构的指针,而这个结构保存了了一簇用于操作特定类型键值对的函数,redis会为不同的字典设置不同的类型特定函数

ht数组每一个项都是一个dictht哈希表,一般情况下,字典使用ht【0】而ht【1】则会在rehash的时候使用

 

 

 

哈希算法:

计算键的哈希值,会用到上面提供到的操作特定类型键值对的函数,如hash= dict->type->hasFunction(key)

index =hash & dict->ht[x].sizemask 而计算出的index就是dictht中的dictentry数组中的索引位置 ,具有相同索引的键会通过dictentry的next连接起来
rehash:rehash的原因就是因为随着操作的执行,对于哈希表保存的键值对会逐渐增加或者减少,为了让哈希表的负载因子维持在一个合理的范围内,当哈希表保存的键值对数量太多或者太少时

程序需要对哈希表的代销进行相应的扩展或者收缩

对于扩展操作,将ht[1]的大小为第一个大于等于ht[0].used*2的2n    

如果执行的是收缩操作那么h[1]的大小为第一个大于等于h[0].used的2n 

将h[0]的所有键值对通过rehash(重新计算键的hash值与索引值),将键值对放到ht[1]哈希表的指定位置上,此时ht【0】为空,则释放ht【0】,让ht【1】设置为ht【0】,并创建ht【1】为下一次rehash做准备

哈希表的扩展时机:
服务器目前没有在执行BGSAVE命令或者BGREWRITEAOF命令,并且哈希表负载因子>=1

服务器目前没有在执行BGSAVE命令或者BGREWRITEAOF命令,并且哈希表负载因子>=5

负载因子计算方式:

load_factor =ht[0].used/ht[0].size

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

当负载因子<0.1时,程序自动开始对哈希表执行收缩操作

此时还有一个坑就是当指向BGSAVE 还有BGREWRITEAOF时为何服务器会提高执行扩展操作所需负载因子,从而尽可能地避免在子进程存在期间进行哈希表的扩展操作,这可以避免必要额内存写入操作,最大限度的节约内存。

更新:明白了这样虽然减少了额外内存,但是也会使hash表键值对分布不会较好的满足离散分布,使得每个dictentry上挂着太多的键值对

 

渐进式哈希:为了避免收缩或者扩展需要进行的rehash操作如果键值对过多对于服务器性能的影响,将rehash操作非一次性,集中式地完成,而是分多次,渐进式的完成

过程如下:在rehash期间,让rehashindex的值为0,每次对字典进行添加,删除,查找,或者更新操作时,程序除了执行指定操作,还会降ht【0】rehashindex索引上的所有键值对rehash到ht【1】

当每一次的rehash完成时,会让rehashindex的值增加1,最后当rehashidnex的值到达used时,全部rehash完成,重新让rehashindex值变为-1

 

posted @ 2020-10-07 20:58  xzwcomeon  阅读(114)  评论(0编辑  收藏  举报