Redis Hash存储结构

作者: 8033b4d1f3ec | 来源:发表于2018-06-20 18:06 被阅读17次

    在前一篇讲了string的存储结构,了解了string的特点及缺陷。相比于string浪费内存的一些缺陷,hash结构很大程度上弥补了string的缺陷。

    hash存储结构具有两大优点,首先就是查询飞快,众所周知hash表的查询复杂度为O(1),可以提供快速查询的业务。第二则是节约内存,业务场景中如果存储的是数据集,则可以存入hash数组节约内存。

    这里说一下hash的存储。假设现在需要存储用户信息,用户信息存在很多用户属性,例如id,姓名,城市……..如果用string存储可能会存在以下的一些问题。存储结构我们会把用户id作为key,用户的所有属性序列化成json存储为value,例如下图所示:


    image2.jpg.jpeg

    每当我们修改用户属性的时候,必须先把用户的所有属性发序列化,再找到其中某一个属性进行修改,修改完成之后,再把修改完的数据序列化成json,进行set操作。整个操作其实是存在开销的,尤其是属性非常多的时候。此外可能还会出现锁的问题。

    那么换成hash存储呢?key依然是用户id,只不过属性变成了hash的field,属性值为hash的value,如下图:


    image4.jpg.jpeg

    这样的存储结构,我们就可以直接修改对应的属性了,并且不会带来序列化和反序列化的开销。

    hash的过期时间,redis的过期时间是针对key的,所以hash的过期时间也是针对key 的。但是有些时候,我们在field中存储了不同的数据,每个field的过期时间是不一样的,如果我们针对key做过期时间,那么整个key就会过期。这样导致下次我们在做初始化数据的时候,代价就非常大。redis本身目前并没有提供针对field做过期时间的设计,但是这是可以实现的。

    实现方式就是,我们增加一个额外的field字段,记录field的过期时间,每次做hget的时候,判断下这个field的过期时间,这样就解决了针对field的过期时间设置。这个设计在某些场景下是非常有用的。


    WechatIMG94.jpeg

    hget,hgetall,hmget。大多数场景用hget就够了,因为hash的应用场景是用来快速定位field的。但是某些场景下我们可能需要获取所有的field,这里需要特别注意下,当你明确能知道有多少参数的时候,那就把field参数传入用hmget。

    如果不知道field参数,只能用hgetall命令,那么得知道hgetll命令是有风险的。当hash存储了非常多的field的时候,redis内部是采用遍历的方式取出的,由于redis是单线程的缘故,很可能会阻塞其他的请求。在大量qps查询的场景中这是非常不适合的。

    hash结构内部并不是所有的存入数据都会用hashmap存储,当hash的成员比较少时,Redis为了节省内存会采用数组的方式来存储,而不会采用真正的HashMap结构。当数量增多的时候,redis内部才会使用hashmap结构存储,这个设计非常精妙,个人觉得在jdk中的hashmap也可以用这样的设计思路。

    相关文章

      网友评论

        本文标题:Redis Hash存储结构

        本文链接:https://www.haomeiwen.com/subject/zfwxyftx.html