redis的字典结构如上图,
字典结构底层采用hash实现,目前hash值得计算采用的是murmurhash2算法,
1,redis采用的hash
table是一个数组,数组中的每个元素都会存储一个指向dictEntry结构的指针,
dictentry结构。
dictentry结构的v可以存储一个指针,也可以存储一个uint64或者int64的整数,此外他还存储了指向下一个节点的指针。这样就解决了hash冲突的问题,跟java1.8之前的hash解决冲突的方式一样,都是维护了一个链表。
dictht中的size代表hash数组的大小。sizemask总是为size-1,是为了计算索引值。used代表hash表内已有节点的数量。
2,字典的结构
字典结构中的type和privdata存储的是对不同类型的键值对的操作,type指向一个叫做dicttype的结构,这个结构里存储了对不同类型键值对操作的函数,而pricdata则是存储了操作这些函数所需要的可选参数。
ht指的是有两个元素的数组,每个元素都是一张hash表,h[0]是存储数据的hash表,h[1]是在rehash的时候使用的。
rehashidx默认=-1代表没有进行rehash,当开始扩容或者收缩时,rehashidx就会记录h[0]中的数据正在转移的下标,转移完成后,值重新为-1;
3 字典的rehash机制、
负载因子= used/size,也就是hash表中已有的节点数/数组的大小。
假设used的大小为a=4.
1,扩容:
1,当redis在执行bgsave或者bgrewriteaof命令时,负载因子大于等于5.
2,没有执行时,负载因子大于等于1.
:因为这两个命令是采用写时复制创建的子线程执行的,也就是说如果没有写入,两个线程共用数据,当进行写操作时,会给子线程分配内存,来区分。因此,在rehash的时候会多创建大量的内存,应该尽量避免在这时候进行rehash。
扩容后的大小为第一个大于等于a*2的2的n次方。也就是第一个大于等于8的2的n次方。也就是2的3次方=8.,
2,收缩,负载因子小于0.1
收缩后的大小为第一个大于等于a的2的n次方,也就是2的2次方=4.
3,rehash的过程
rehash是渐进的,在挪动过程中,所有的添加都会直接添加到h[1]上,所有rehash过来的键值对都会重新计算hash值,查询的时候先查询h[0],如果没有在查询h[1].
1.redis当发现需要扩容或者收缩时,就会按照需要操作的类型给h[1]分配空间。
2.将rehashidx的值设置为0,
3.在rehash期间,所有对h[0]的增删改查都会顺带将rehashidx索引对应的键值对给rehash到h[1]上,结束后rehashidx的值加一。所以称之为渐进。只有在有操作的时候才会执行。
4,直到h[0]的used为0;这时将rehashidx的值设置为-1代表已经结束,同时释放h[0],将h[1]设置为h[0],并给新的h[0]设置h[1]。