本章接着上一节继续介绍 Redis 的基础数据结构中的Hash字典.java
Hash 也能够用来存储用户信息,和 String 不一样的是 Hash 能够对用户信息的每一个字段单独存储,String 则须要序列化用户的全部字段后存储.而且 String 须要以整个字符串的形式获取用户,而 hash能够只获取部分数据,从而节约网络流量.不过 hash 内存占用要大于 String,这是 hash 的缺点.golang
> hset books java "Effective java" (integer) 1 > hset books golang "concurrency in go" (integer) 1 > hget books java "Effective java" > hset user age 17 (integer) 1 >hincrby user age 1 #单个 key 能够进行计数 和 incr 命令基本一致 (integer) 18
Redis 中的 Hash和 Java的 HashMap 更加类似,都是数组+链表的结构.当发生 hash 碰撞时将会把元素追加到链表上.值得注意的是在 Redis 的 Hash 中 value 只能是字符串.数组
看完基本介绍以后,咱们先来了解下 hash 的内部结构.第一维是数组,第二维是链表.组成一个 hashtable.网络
部分源码:数据结构
struct dictht { dictEntry **table; //entry 数组 long size; //数组长度 long used //数组中的元素个数 ... } struct dictEntry{ void *key; //hash 的 key void *val; //hash 的 value dictEntry *next; //下一个dictEntry 链表结构 }
在 Java 中 HashMap 扩容是个很耗时的操做,须要去申请新的数组,为了追求高性能,Redis 采用了渐进式 rehash
策略.这也是 hash 中最重要的部分.性能
在 hash 的内部包含了两个hashtable,通常状况下只是用一个.如图所示:spa
在扩容的时候 rehash 策略会保留新旧两个 hashtable 结构,查询时也会同时查询两个 hashtable.Redis会将旧 hashtable 中的内容一点一点的迁移到新的 hashtable 中,当迁移完成时,就会用新的 hashtable 取代以前的.当 hashtable 移除了最后一个元素以后,这个数据结构将会被删除.如图所示:code
数据搬迁的操做放在 hash 的后续指令中,也就是来自客户端对 hash 的指令操做.一旦客户端后续没有指令操做这个 hash.Redis就会使用定时任务对数据主动搬迁.内存
正常状况下,当 hashtable 中元素的个数等于数组的长度时,就会开始扩容,扩容的新数组是原数组大小的 2 倍.若是 Redis 正在作 bgsave(持久化) 时,可能不会去扩容,由于要减小内存页的过多分离(Copy On Write).可是若是 hashtable 已经很是满了,元素的个数达到了数组长度的 5 倍时,Redis 会强制扩容.rem
当hashtable 中元素逐渐变少时,Redis 会进行缩容来减小空间占用,而且缩容不会受 bgsave 的影响,缩容条件是元素个数少于数组长度的 10%.