1.3.3、dict
1、dictType 类型,包括一些自定义函数,这些函数使得key和value能够存储
2、rehashidx 其实是一个标志量,如果为-1说明当前没有扩容,如果不为 -1 则记录扩容位置。
3、dictht数组,两个Hash表。
4、iterators 记录了当前字典正在进行中的迭代器
组合后结构就是如下:
1.3.4、渐进式扩容
为什么 dictht ht[2]是两个呢?目的是在扩容的同时不影响前端的CURD,慢慢的把数据从ht[0]转移到ht[1]中,同时rehashindex来记录转移的情况,当全部转移完成,将ht[1]改成ht[0]使用。
rehashidx = -1说明当前没有扩容,rehashidx != -1则表示扩容到数组中的第几个了。
扩容之后的数组大小为大于used*2的2的n次方的最小值,跟 HashMap 类似。然后挨个遍历数组同时调整rehashidx的值,对每个dictEntry[i] 再挨个遍历链表将数据 Hash 后重新映射到 dictht[1]里面。并且 dictht[0].use 跟 dictht[1].use 是动态变化的。
整个过程的重点在于rehashidx,其为第一个数组正在移动的下标位置,如果当前内存不够,或者操作系统繁忙,扩容的过程可以随时停止。
停止之后如果对该对象进行操作,那是什么样子的呢?
1、如果是新增,则直接新增后第二个数组,因为如果新增到第一个数组,以后还是要移过来,没必要浪费时间
2、如果是删除,更新,查询,则先查找第一个数组,如果没找到,则再查询第二个数组。
1.4、Set
如果你明白Java中HashSet是HashMap的简化版那么这个Set应该也理解了。都是一样的套路而已。这里你可以认为是没有Value的Dict。看源码 t.set.c 就可以了解本质了。
intsetTypeAdd(robj *subject, robj *value){
longlongllval;
if(subject->encoding == REDIS_ENCODING_HT) {
// 看到底层调用的还是dictAdd,只不过第三个参数= NULL
if(dictAdd(subject->ptr,value,NULL) == DICT_OK) {
incrRefCount(value);
return1;
}
....
1.5、ZSet
范围查找 的天敌就是 有序集合,看底层 redis.h 后就会发现 Zset用的就是可以跟二叉树媲美的跳跃表来实现有序。跳表就是多层链表的结合体,跳表分为许多层(level),每一层都可以看作是数据的索引,这些索引的意义就是加快跳表查找数据速度。
每一层的数据都是有序的,上一层数据是下一层数据的子集,并且第一层(level 1)包含了全部的数据;层次越高,跳跃性越大,包含的数据越少。并且随便插入一个数据该数据是否会是跳表索引完全随机的跟玩骰子一样。
跳表包含一个表头,它查找数据时,是从上往下,从左往右进行查找。现在找出值为37的节点为例,来对比说明跳表和普遍的链表。
没有跳表查询 比如我查询数据37,如果没有上面的索引时候路线如下图:
有跳表查询 有跳表查询37的时候路线如下图:
应用场景:
积分排行榜、时间排序新闻、延时队列。