来谈谈第三种基本数据结构Hash(字典)
hash的底层实现
它的底层实现,跟java的hashmap一样,也是数组+链表的组合。
不过,redis的hash的值只能是字符串。
如何rehash
渐进式rehash。构造一个新的hash,将老hash逐渐转移到新hash上,在此期间,老hash还会被客户端使用。
redis hash数据结构
当然还有我们的对象头
struct RedisObject {
int4 type;//4bit,对象类型,那最多最多16种对象咯
int4 encoding;//存储形式
int24 lru;//Least Recently Used,最近最少使用信息
int32 refcount;//暂未知,之后补上,todo
void *ptr;//指向字典数据
}
struct dict {
...
dictht ht[2]
}
一般只有ht[0]有数据,只有在扩容的时候,ht[1]才会新建一个字典结构
ht=hashtable
struct dictht {
dictEntry** table;//二维数组
long size;//第一维数组的长度,即有多少个槽位
long used;//hash表中的元素个数,因为会有槽位冲突,而产生的链表
}
struct dictEntry {
void* key;
void* val;
dictEntry* next;
}
大概内存里是这样的结构[[dictEntry,dictEntry],[dictEntry]]
如何触发rehash
hset,hdel,除了增加删除操作,还会有定时操作进行搬迁。
redis的默认hash函数——siphash
因为hash存在偏向性,如果被黑客注入hash值都相同的不同的值,导致hash的链表非常长,那么搜索效率会从O(1)变成O(n),拖垮计算机的性能。
何时?扩容
一般元素达到第一维的长度的话,就会进行扩容,但是当时如果在bgsave那么会尽量不扩容。但是如果存储的元素,达到了一维长度的5倍,那么就会强制扩容。
何时?缩容
有扩容也会有缩容,都是为了匹配最佳的存储空间。
当存储元素只占数组长度的10%时,就会进行缩容,并且并不考虑bgsave。
rehash与bgsave
这两者内容都是对hash表进行重排。在集群那一章,我们会着重讲解。