字典在redis中的应用广泛,redis的数据库就是使用字典来作为底层实现的,对数据库的增、删、查、改操作也是构建在对字典的操作之上的。
举个例子,当我们执行命令:
redis> SET msg "hello world"
OK
在数据库中创建一个键为"msg",值为”hello world“的键值对,这个键值对就是保存在代表数据库的字典里。
redis的字典使用哈希表作为底层实现,一个哈希表里面可以有多个哈希表节点,每个哈希表节点就保存了字典中的一个键值对。
- 哈希值: 使用
MurmurHash2
算法。 - 冲突:使用链地址法解决键冲突。
- load factor: 为了让 load factor 维持在一个合理的范围之内,当超出这个范围就对哈希表进行相应的
扩张
或者收缩
(rehash)。 - 字典使用哈希表作为底层实现,每个字典带有两个哈希表,一个平时使用,一个仅在进行 rehash 时使用。
redis rehash 的步骤如下:
- 为字典的ht[1]哈希表分配空间。空间的大小设置: 1)扩展:第一个大于等于 ht[0].used*2 的数(大小必须为2^n); 2)收缩:第一个大于等于 ht[0].used 的数(大小必须为2^n)。
- 将保存在ht[0]中的所有键值对放置到ht[1]上,这个过程是不是一次性、集中式地完成的,采取分而治之的方式,将 rehash 的计算工作均摊到对字典的每个添加、删除、查找和更新操作上,从而避免其中式 rehash 而带来的庞大计算量。
- 当 ht[0] 包含的所有键值对都迁移到 ht[1] 之后(ht[0]变为空表),释放ht[0],将 ht[1] 设置为 ht[0],并在 ht[1] 新创建一个空白Hash表,为下一次 rehash 做准备。
在 rehash 过程中,字典会同事使用 ht[0] 和 ht[1] 两个哈希表,delete, find, update 等操作会在两个哈希表上进行。
例如,查找时,会先在 ht[0] 里面进行查找,如果没有找的话,就会继续到 ht[1] 里面进行查找。
添加键值对 会被保存到 ht[1] 里面,而 ht[0] 则不再进行任何添加操作,这一措施保证了 ht[0] 包含的键值对数量会只减不增,并随着 rehash 操作的执行而最终变成卡表。