Redis对象——哈希(Hash)

系列文章

最新:Redis持久化——如何选择合适的持久化方式html

最新:Redis持久化——AOF日志redis

最新:Redis持久化——内存快照(RDB)数据库

一文回顾Redis五大对象(数据类型)编程

Redis对象——有序集合(ZSet)c#

Redis对象——集合(Set)浏览器

Redis对象——列表(List)缓存

Redis对象——哈希(Hash)cookie

Redis数据结构——quicklist数据结构

Redis对象——字符串运维

Redis对象——Redis对象系统简介

Redis数据结构——压缩列表

Redis数据结构——整数集合

Redis数据结构——跳跃表

Redis数据结构——字典

Redis数据结构——链表

Redis数据结构——简单动态字符串SDS

哈希在不少编程语言中都有着很普遍的应用,而在Redis中也是如此,在redis中,哈希类型是指Redis键值对中的值自己又是一个键值对结构,形如value=[{field1,value1},...{fieldN,valueN}],其与Redis字符串对象的区别以下图所示:

Redis-Hash

1、内部编码

    哈希类型的内部编码有两种:ziplist(压缩列表),hashtable(哈希表)。只有当存储的数据量比较小的状况下,Redis 才使用压缩列表来实现字典类型。具体须要知足两个条件:

  • 当哈希类型元素个数小于hash-max-ziplist-entries配置(默认512个)

  • 全部值都小于hash-max-ziplist-value配置(默认64字节)
    ziplist使用更加紧凑的结构实现多个元素的连续存储,因此在节省内存方面比hashtable更加优秀。当哈希类型没法知足ziplist的条件时,Redis会使用hashtable做为哈希的内部实现,由于此时ziplist的读写效率会降低,而hashtable的读写时间复杂度为O(1)。
    有关ziplist和hashtable这两种redis底层数据结构的具体实现能够参考个人另外两篇文章。

    Redis数据结构——压缩列表

    Redis数据结构——字典

2、经常使用命令

Redis哈希对象经常使用命令以下表(点击命令可查看命令详细说明)。

命令 说明 时间复杂度
HDEL key field [field ...] 删除一个或多个Hash的field O(N) N是被删除的字段数量。
HEXISTS key field 判断field是否存在于hash中 O(1)
HGET key field 获取hash中field的值 O(1)
HGETALL key 从hash中读取所有的域和值 O(N) N是Hash的长度
HINCRBY key field increment 将hash中指定域的值增长给定的数字 O(1)
HINCRBYFLOAT key field increment 将hash中指定域的值增长给定的浮点数 O(1)
HKEYS key 获取hash的全部字段 O(N) N是Hash的长度
HLEN key 获取hash里全部字段的数量 O(1)
HMGET key field [field ...] 获取hash里面指定字段的值 O(N) N是请求的字段数
HMSET key field value [field value ...] 设置hash字段值 O(N) N是设置的字段数
HSET key field value 设置hash里面一个字段的值 O(1)
HSETNX key field value 设置hash的一个字段,只有当这个字段不存在时有效 O(1)
HSTRLEN key field 获取hash里面指定field的长度 O(1)
HVALS key 得到hash的全部值 O(N) N是Hash的长度
HSCAN key cursor [MATCH pattern] [COUNT count] 迭代hash里面的元素

3、适用场景

3.1 存储对象

​ Redis哈希对象经常用来缓存一些对象信息,如用户信息、商品信息、配置信息等。

咱们以用户信息为例,它在关系型数据库中的结构是这样的

uid name age
1 Tom 15
2 Jerry 13

而使用Redis Hash存储其结构以下图:

相比较于使用Redis字符串存储,其有如下几个优缺点:

  1. 原生字符串每一个属性一个键。

    set user:1:name Tom
    set user:1:age 15

    优势:简单直观,每一个属性都支持更新操做。
    缺点:占用过多的键,内存占用量较大,同时用户信息内聚性比较差,因此此种方案通常不会在生产环境使用。

  2. 序列化字符串后,将用户信息序列化后用一个键保存

    set user:1 serialize(userInfo)

    优势:简化编程,若是合理的使用序列化能够提升内存的使用效率。
    缺点:序列化和反序列化有必定的开销,同时每次更新属性都须要把所有数据取出进行反序列化,更新后再序列化到Redis中。

  3. 序列化字符串后,将用户信息序列化后用一个键保存

    hmset user:1 name Tom age 15

    优势:简单直观,若是使用合理能够减小内存空间的使用。
    缺点:要控制哈希在ziplist和hashtable两种内部编码的转换,hashtable会消耗更多内存。

此外,咱们曾经在作配置中心系统的时候,使用Hash来缓存每一个应用的配置信息,其在数据库中的数据结构大体以下表

AppId SettingKey SettingValue
10001 AppName myblog
10001 Version 1.0
10002 AppName admin site

在使用Redis Hash进行存储的时候

新增或更新一个配置项

127.0.0.1:6379> HSET 10001 AppName myblog
(integer) 1

获取一个配置项

127.0.0.1:6379> HGET 10001 AppName 
"myblog"

删除一个配置项

127.0.0.1:6379> HDEL 10001 AppName
(integer) 1

3.2 购物车

    不少电商网站都会使用 cookie实现购物车,也就是将整个购物车都存储到 cookie里面。这种作法的一大优势:无须对数据库进行写入就能够实现购物车功能,这种方式大大提升了购物车的性能,而缺点则是程序须要从新解析和验证( validate) cookie,确保cookie的格式正确,而且包含的商品都是真正可购买的商品。cookie购物车还有一个缺点:由于浏览器每次发送请求都会连 cookie一块儿发送,因此若是购物车cookie的体积比较大,那么请求发送和处理的速度可能会有所下降。

    购物车的定义很是简单:咱们以每一个用户的用户ID(或者CookieId)做为Redis的Key,每一个用户的购物车都是一个哈希表,这个哈希表存储了商品ID与商品订购数量之间的映射。在商品的订购数量出现变化时,咱们操做Redis哈希对购物车进行更新:

若是用户订购某件商品的数量大于0,那么程序会将这件商品的ID以及用户订购该商品的数量添加到散列里面。

//用户1 商品1 数量1
127.0.0.1:6379> HSET uid:1 pid:1 1
(integer) 1 //返回值0表明改field在哈希表中不存在,为新增的field

若是用户购买的商品已经存在于散列里面,那么新的订购数量会覆盖已有的订购数量;

//用户1 商品1 数量5
127.0.0.1:6379> HSET uid:1 pid:1 5
(integer) 0 //返回值0表明改field在哈希表中已经存在

相反地,若是用户订购某件商品的数量不大于0,那么程序将从散列里面移除该条目。

//用户1 商品1
127.0.0.1:6379> HDEL uid:1 pid:2
(integer) 1

3.3 计数器

    Redis 哈希表做为计数器的使用也很是普遍。它经常被用在记录网站每一天、一月、一年的访问数量。每一次访问,咱们在对应的field上自增1

//记录个人
127.0.0.1:6379> HINCRBY MyBlog  202001 1
(integer) 1
127.0.0.1:6379> HINCRBY MyBlog  202001 1
(integer) 2
127.0.0.1:6379> HINCRBY MyBlog  202002 1
(integer) 1
127.0.0.1:6379> HINCRBY MyBlog  202002 1
(integer) 2

也常常被用在记录商品的好评数量,差评数量上

127.0.0.1:6379> HINCRBY pid:1  Good 1
(integer) 1
127.0.0.1:6379> HINCRBY pid:1  Good 1
(integer) 2
127.0.0.1:6379> HINCRBY pid:1  bad  1
(integer) 1

也能够实时记录当天的在线的人数。

//有人登录
127.0.0.1:6379> HINCRBY MySite  20200310 1
(integer) 1
//有人登录
127.0.0.1:6379> HINCRBY MySite  20200310 1
(integer) 2
//有人登出
127.0.0.1:6379> HINCRBY MySite  20200310 -1
(integer) 1

小结

本篇文章咱们总结了Redis 哈希对象的内部实现、经常使用命令以及经常使用的一些场景,那么你们在项目中对Redis哈希对象的使用都有哪些场景呢,欢迎在评论区给我留言和分享,我会第一时间反馈!咱们共同窗习与进步!

参考

《Redis设计与实现》

《Redis开发与运维》

《Redis官方文档》

-----END-----

关注下方公众号,回复“Redis”,可得Redis相关学习资料

相关文章
相关标签/搜索