基本介绍
Hash 也可以用来储存使用者资讯,和 String 不同的是 Hash 可以对使用者资讯的每个字段单独储存,String 则需要序列化使用者的所有字段后储存.并且 String 需要以整个字串的形式获取使用者,而 hash可以只获取部分资料,从而节约网络流量.不过 hash 内存占用要大于 String,这是 hash 的缺点.
> 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 中最重要的部分。
渐进式 rehash
在 hash 的内部包含了两个hashtable,一般情况下只是用一个.如图所示:
在扩容的时候 rehash 策略会保留新旧两个 hashtable 结构,查询时也会同时查询两个 hashtable.Redis会将旧 hashtable 中的内容一点一点的迁移到新的 hashtable 中,当迁移完成时,就会用新的 hashtable 取代之前的.当 hashtable 移除了最后一个元素之后,这个资料结构将会被删除.如图所示:
资料搬迁的操作放在 hash 的后续指令中,也就是来自客户端对 hash 的指令操作.一旦客户端后续没有指令操作这个 hash.Redis就会使用定时任务对资料主动搬迁.
正常情况下,当 hashtable 中元素的个数等于阵列的长度时,就会开始扩容,扩容的新阵列是原阵列大小的 2 倍.如果 Redis 正在做 bgsave(持久化) 时,可能不会去扩容,因为要减少内存页的过多分离(Copy On Write).但是如果 hashtable 已经非常满了,元素的个数达到了阵列长度的 5 倍时,Redis 会强制扩容。
当hashtable 中元素逐渐变少时,Redis 会进行缩容来减少空间占用,并且缩容不会受 bgsave 的影响,缩容条件是元素个数少于阵列长度的 10%。
需要的Java架构师方面的资料可以关注之后私信哈,回复“资料”领取免费架构视讯资料,记得要点赞转发噢!!!