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