系列文章:
Redis的值是Hash类型(又可以称为字典Dict)底层采用哈希表实现,一个哈希表有多个哈希表节点,每个 哈希表节点就保存了一个键值对
哈希表的结构:
一个大小为4的空哈希表结构,如下图所示
table是一个数组,数组中每个元素都指向一个哈希表节点
size属性记录了哈希表的大小,也就是table数组的大小
sizemask属性的值总是等于size-1,这个属性和哈希值一起决定一个键应该被放到table数组的哪个索引上面
used属性记录了table数组中非null的数目,非null即指针已经对应了一个哈希表节点
哈希节点结构:
用dictEntry表示,每个dictEntry结构保存着一个键值对
K属性保存键
V属性保存值,值可以是一个指针,或者是数值
next属性是指向另一个哈希表节点的指针,利用指针可以将多个哈希值相同的键值对连接在一起,以此解决了键冲突的问题
Redis 中采用字典dict又对哈希表进行了一层包装,dict的结构如下
type属性是一个指向dictType结构的指针,每个dictType结构保存了一簇用于操作特定类型键值对的函数,Redis会为用途不同的字典设置不同的类型特定函数.
privdata属性保存了需要传给那些类型特定函数的可选参数
ht属性是一个包含两个项的数组,正常情况下,字典只使用ht[0]哈希表,当哈希表在rehash的时候,会使用ht[1]哈希表.
rehashidx记录了rehash的进度,如果目前没有rehash,它的值为-1
哈希算法:
当一个新的键值对被添加到字典里时,需要根据键值对的键计算出哈希值和table数组的索引值,根据索引值,将键值对添加到数组的指定位置上.
1.利用字典设置的hash函数,计算key的哈希值
2.索引值 = 哈希值 & sizemask (二进制作与运算)
rehash操作:
随着不断添加或删除键值对,为了让哈希表的负载因子维持在一个合理的范围内,需要对哈希表进行rehash操作(利用ht[1]的哈希表)
1.为字典ht[1]哈希表分配空间,空间的大小取决于是扩展还是收缩,以及ht[0]当前包含的键值对数量(即ht[0].used属性的值)
(1) 如果要扩展哈希表,首先计算 x = ht[0].used*2的值,然后找到第一个大于等于x的 2的n次方的值y , y就是ht[1]应该分配的空间大小.
例如 x = 5*2, 那么y 应该是2的4次方 = 16 是第一个大于等于x的2的n次方,所以y = 16,.
(2) 如果是缩减哈希表 x = ht[0].used , 然后找到第一个大于等于x的 2的n次方的值y , y就是ht[1]应该分配的空间大小.
例如 x = 5 , 那么y 应该是2的3次方 = 8 是第一个大于等于x的2的n次方,所以y = 8
2.将ht[0]中所有键值对重新计算索引,放到ht[1]哈希表的指定位置上
3.当ht[0]所有键值对迁移到ht[1]的工作完成后(ht[0]变成空表),释放ht[0],将ht[1]设置为ht[0],并为ht[1]创建一个空白哈希表,为下一次rehash作准备.
Hash常用语法简介:
HSET
往字典中添加键值对 一个字典可以添加一个键值对,也可以是多个
语法:
HSET key field value
HGET
获取字典中某个键(key) 对应的value
语法:
HGET key field
HMGET
获取字典中多个键(key1 key2 …) 对应的多个value
语法:
HMGET key field [field ...]
HGETALL
得到字典中所有的键值对
语法:
HGETALL key
HLEN
获得字典中所有键值对的数目
语法:
HLEN key
HEXISTS
判断某个字典的所有key中是否包含某个值
语法:
HEXISTS key field
HKEYS
显示字典中所有key值
语法:
HKEYS key
HVALS
显示字典中的所有value值
语法:
HVALS key