【说明:本系列文章所有思维导图均为纯手工打造,转载请注明出处】
关于部分热心朋友提醒的几点说明
1)有朋友反映说为啥我的博客很多都是只放一张图,对读者很不友好。这里说明下原因:
- 本人很懒加上时间有限,为了保持博客编写速度尽可能高效, 觉得思维导图在逻辑结构上更直观,图能表达的内容,不愿意再用文字赘述一遍;
- 本人习惯用思维导图的形式串联知识点,觉得通过文档目录来串联知识点并体现层级结构的方式比较麻烦;
- 本人博客所有内容均为自主学习过程中形成的思维导图笔记。
2)有朋友反映说思维导图无法很好的承载表现一些复杂的内容:
- 这个确实,哈哈,无法反驳;
- 本人由于水平有限,思维导图大部分内容可看作各相关知识点的一个概述,所有通过思维导图没有讲到、或者讲解的不够清楚有纰漏、或者不够深入的地方可通过别的途径进行补充学习,更伟大的做法是“评论区留言,大家一起探讨学习”
1.关键数据结构及元素
- Redis字典的结构
dictType *type; //类型特定函数
void *privdata; //私有数据
dictht ht[2]; //哈希表
in trehashidx; //rehash索引,没有进行Rehash值为-1
- dictht的结构:
{
dictEntry **table; //哈希表数组
unsigned long size; //哈希表大小
unsigned long sizemark;//大小掩码
unsigned long used; //已使用节点数量
};
- dictEntry结构:
{
void* key;
struct dictEntry *next;
union{
void *val; unit64_tu64; int64_ts64;
} v;
};
- 字典负载因子:ht[0].used/ht[0].size
2.rehash概述
2.1 目的
随着哈希表保存键值对的主键增多或减少,为了使哈希表的负载因子维持在一个合理范围,当哈希表保存的键值太多或者太少时,程序会对哈希表的大小进行响应的扩展或者收缩。
2.2.触发条件
2.2.1扩张触发条件
- 服务器没有执行BGSAVE或者BGREWRITEOF命令,并且负载因子大于1;
- 服务器在执行BGSAVE 或者BGREWRITEOF命令,并且负载因子大于等于5
为什么在执行BGSAVE时,会提升负载因子的阈值?
为了避免在执行BGSAVE时执行扩张,最大限度的节约内存。
2.2.2收缩触发条件
负载因子小于0.1时
2.3rehash流程
1)为字典的ht[1]哈希表分配空间,空间大小取决于要执行的操作和ht[0]当前包含的键值对数量
如果是扩展操作,ht[1]的大小为第一个大于等于ht[0].used*2的2的n次方幂。例如ht[0].used=10,那么大于ht[0].used*2=20的第一个2的n次方幂是32。
如果是收缩操作,ht[1]的大小为第一个大于等于ht[0].used的2的n次方幂。例如ht[0].used=10,那么大于ht[0].used的第一个2的n次方幂是16。
2)将保存在ht[0]中的所有键值rehash到ht[1]上(采用的时渐进式hash)
3)当ht[0]所有的键值都迁移到ht[1]后,释放ht[0],并将ht[1]设置为ht[0],并在ht[1]新建一个空白的哈希表,为下次rehash做准备。
2.4渐进式hash
2.4.1为什么需要渐进式hash
为了防止当字典中数据量过大,执行rehash会严重消耗系统资源,甚至导致服务器停止服务。
2.4.2运行流程
1)为ht[1]分配空间,具体参见上面rehash步骤1的说明
2)同时将字典结构中的索引计数器rehashidx字典值置为0,表示rehash工作正式开始;
3)每次对字典执行增删改查时,会将ht[0]哈希表在rehashidx索引上的所有键值对reshah到ht[1](也就是每次rehash一个桶的数据),当rehash完成后,rehashidx值加1(使下次rehash时操作的是下一个桶的数据)。
4)当某个时间点,ht[0]所有的键值对都被rehash到ht[1]时,将rehashidx的值置为-1,表示rehash完成。