初步了解Redis(二) | 青训营笔记

65 阅读2分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第10天

Hash

hash可以等价于go中的map, Java中的HashMap, 本质上是使用散列表的一个数据结构

比较学术的说法是是散列函数: 一个把查找表中的关键字映射成该关键字对应的地址的函数, 记为Hash(key)=Addr(这里的地址可以是数组下标, 索引或者内存地址)

Hash函数的构造, 必须注意以下几点,

  1. Hash函数计算得到的地址, 必须等概率, 均匀的分布在整个地址空间中, 用以减少冲突的发生
  2. 散列函数应该尽量简单, 使得可以很快地计算出对应地散列地址

综上, 目前最常用地散列函数, 便是除留余数法, 即将关键字对散列表地地址空间进行取余操作, 使用余数来确定该关键字地地址

既然是使用余数定址, 那么必定会出现冲突, 即两个关键字位置在同一个地方

这时, Redis采用的解决方法是拉链法, 即把每个地址当作一个单链表的head节点, 使用单链表存储冲突元素

f3ed6ba7d46a0daa6050476b9401e8e

地址空间大小

另外需要值得注意的是, 散列表的长度一般为2^n, 这是考虑到取余的计算效率问题

如上所示, 我们一般使用的散列函数是直接对关键字取余(如果关键字是字符串的话, 就将字符串按照某种规则映射到散列表上)

而当被mod的数是2^n时, 可以使用位运算&(2^n-1)来进行快速取余

eg, 当n=3时, 则%8和&(111)的效果是一样的, 因此散列表大小一般取2^n

扩容和缩容

当hash内部元素较为拥挤时, 即hash碰撞较为频繁, 此时需要对散列表进行扩容. Redis扩容需要申请两倍大小的地址空间, 然后将所有的键值对重新分配到新的地址空间中.

当hash结构非常大时, 重新hash的开销就会变大, 此时Redis采用了渐进式reHash的方式, 即同时保留新旧两个地址空间, 在后续查询中, 将查到的的关键字转移到新的地址空间中.

同扩容一样, 缩容的实现方式几乎相同, 只有重新申请的地址空间大小变为原先地址空间的一半, 其余操作完全相同