redis数据类型之hash(字典)
常规操作
1、批量操作
> hmset numbers fir "one" sec "two" thir "three"
OK
> hgetall numbers
1) "fir"
2) "one"
3) "sec"
4) "two"
5) "thir"
6) "three"
2、单一属性操作
> hset numbers fir "one num" //字符串包含空格,则必须用引号括起来
(integer) 1
> hset numbers sec "two"
(integer) 1
> hset numbers thir "three"
(integer) 1
> hlen numbers //获取字典元素个数
(integer) 3
> hget numbers fir
"one num"
> hset numbers fir "one" //更新操作,返回值为0
(integer) 0
> hget numbers fir
"one"
3、单个属性计数
> hset numbers count 5
(integer) 1
> hincrby numbers count 1
(integer) 6
内部结构
hash的内部结构为“数组+链表” 的二维结构。
当第一维hash的数组位置碰撞时,就会将碰撞的元素使用链表串接起来。
redis的hash的值只能是字符串。
hash函数
hash函数会将key映射为一个整数。hashtable的性能好不好完全取决于hash函数的质量,当hash函数将key打散得比较均匀,那么这个hash函数是个好函数。redis默认的hash函数为siphash,该函数性能非常好。
渐进式rehash
采用渐进式rehash的目的是为了提高性能,不堵塞服务。
其渐进过程如下图:
渐进式rehash会在rehash时保留新旧两个hash结构,查询时会同时查询连个hash结构,然后在后续定时任务以及hash操作指令(来自客户端的hset、hdel等指令)中循序渐进的将旧hash的内容一点点的迁移到新的hash结构中。当搬迁完成,就会使用新的hash结构代替旧的。当旧的hash结构最后一个元素被移除时,改数据结果自动删除,内存被回收。
hash攻击
当hash函数存在偏向性时,利用这个偏向性向服务器进行攻击,最终导致hash第二维链表长度极度不均匀,甚至所有元素都集中到个别链表中,直接导致查找效率急剧下降。这就是hash攻击。
扩容条件
正常条件下,当hash表中的元数个数等于第一维数组的长度时,开始扩容,扩容的新数组是原数组大小的2倍。
当redis在做bgsave时,redis会尽量不去扩容(dict_can_resize),除非hash表元素个数达到了第一维数组长度的5倍(dict_forceresize_ratio),此时会强制扩容。
缩容条件
当元素个数低于数组长度的10%时会进行缩容,缩容不会考虑redis是否正在做bgsave。