Redis笔记(三)- 基础数据结构_Hash和Set

Redis的hash结构跟Java的HashMap十分相似,同样都是用数组加链表组成(还是是数组和链表,和上一节的quicklist组成是一样的吧,只不过quicklist的结构是由数组组成的链表,而hash则是由链表组成的数组)。Set内部结构也是hash,只不过hashEntry中所有的 value 都是一个值NULL(又抄我java)。hash表就不多说了,直接上图,哦,这里要注意,hash结构的值只能是string类型(redis中整数什么的都可以视为string)。


hash.png

探索hash字典内部原理

struct hashEntry {
    void* key;
    void* val;
    hashEntry* next; // 下一个 entry的地址值
}
struct hashTable{
    hashEntry** table; // 二维数组,相当于java中的 new Entry[][]
    long size; // 第一维数组的长度,hash表中数组部分的长度
    long used; // hash 表中的所有元素个数
    ...
}

这个二维数组的行代表着hash表中数组的部分的长度,列代表着hash表中的每一个链表的长度。

两个hashTable

实际上,每个hash类型的数据结构里面都会有两个hashTable,就是因为在扩容的时候采取了一种渐进式的扩容方式,这种方式会提前创建一个新的hashtable,容量为当前used*2,触发扩容条件时,将老hashTable的数据分批次迁移到新的hashtable当中,这种方式最大的好处就是不会因为一次性迁移的数据量太大而导致读写的阻塞。(CopyOnWriteArrayList是嘛)

在读请求进来时,如果发现扩容还未结束,会先从老hashTable查,查不到会再从新的hashTable查。而有写请求时,会顺便将这个元素rehash一次,把值直接存到新hashTable中。

扩容时机

1.当hash中元素总个数=hash中数组的长度时,且没有正在进行bgsave或bgrewriteaof操作(持久化内存快照)。
2.当hash中元素总个数=hash中数组的长度的5倍时,即使正在进行bgsave或bgrewriteaof操作,也会强制扩容。(我觉得红黑树还能再挺一下)

缩容时机

当hash中元素总个数=hash中数组的长度的1/10时,便会缩容。

Set中的intset
struct intset {
    uint32_t encoding;//编码方式,可选16/32/64位
    uint32_t length;//数组长度
    int8_t contents[];//元素
}
intset.png

当set内元素个数较少且都为整型时,set内部结构会优化为一个intset,结构和压缩列表类似,不过intset的元素都为整型,在intset中定位一个元素时,会使用二分法查找,时间复杂度为O(logn)。
intset 只能升级,不能降级。将一个大于当前encoding位数的元素插入到intset ,会导致intset升级,或者直接变成hash结构。

你可能感兴趣的:(Redis笔记(三)- 基础数据结构_Hash和Set)