Redis的字典相当于java语言里面的HashMap,如图所示,它是无序字典,内部存储了很多键值对,,实现结构上与Java的HashMap 也是一样,都是“数组 + 链表”二维结构,如下图所示,第一维Hash的数组位置碰撞时,就会碰撞的元素使用链表串联起来。
不同的是,Redis的字典的值只能是字符串,另外它们rehash的方式不一样,因为Java的HashMap在字典很大时,rehash是个耗时的操作,需要一次性全部rehash,Redis为了追求高性能,不能堵塞服务,所以采用了渐进式rehash策略。
渐进式rehash会在rehash的同时,保留新旧两个hash结构,如图所示,查询时会同时查询两个hash结构,然后在后续的定时任务以及hash操作指令中,循环渐进式地将旧hash内容一点点地迁移到新的hash结构中,当搬迁完成了,就会使用新的hash结构取而代之。
当hash移除了最后一个元素之后,该数据结构被自动删除,内存被回收。
hash结构也可以用来存储用户结构信息,与字符串需要一次性全部序列化整个对象不同,hash可以对用户结构中的每个字段单独存储,这样当我们需要获取用户信息时。就可以进行部分获取,而整个字符串的形式去保存用户信息的话,就只能一次性全部读取,这样就会浪费网络流量。
hash也有缺点,hash结构的存储消耗要高于单个字符串,到底该使用hash还是字符串,需要根据实际情况再三权衡。
同字符串一样,hash 结构中对单个字段也可计数操作指令hincrby,和incr 的指令一样。
hash 和string 的使用:
如果存储的都是比较结构化的数据,比如用户数据缓存,或者经常需要操作数据的一个或者几个,特别是如果一个数据中如果filed比较多,但是每次只需要使用其中的一个或者少数的几个,使用hash是一个好的选择,因为它提供了hget 和 hmget,而无需取出所有数据再在代码中处理。
反之,如果数据差异较大,操作时常常需要把所有数据都读取出来再处理,使用string 是一个好的选择。
hash 渐进式rehash的详细步骤:
- 为hash[1]分配空间,让字典同时持有hash[0]和hash[1]两个哈希表。
- 在字典中维持一个索引计数器变量rehashidx,并将它的值设置为0,表示rehash工作正式开始。
- 在rehash执行期间,每次对字典执行添加、删除、查找或者更新操作时,程序除了执行指定的操作外,还会顺带将hash[0]哈希表在rehashidx索引上的所有键值对rehash到hash[1],当rehash工作完成后,程序将rehashidx属性的值增1。
- 随着字典操作的不断执行,最终在某个时间点上,hash[0]的所有键值对都会被rehash至hash[1],这时程序将rehashidx属性的值设为-1,表示rehash操作已完成。