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

一、内部编码

    哈希类型的内部编码有两种: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数据结构——字典

二、常用命令

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.1 存储对象

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

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

uid name age
1 Tom 15
2 Jerry 13

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

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

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

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

    优点:简单直观,每个属性都支持更新操作。

    缺点:占用过多的键,内存占用量较大,同时用户信息内聚性比较差,所以此种方案一般不会在生产环境使用。

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

    1. set user:1 serialize(userInfo)

    优点:简化编程,如果合理的使用序列化可以提高内存的使用效率。

    缺点:序列化和反序列化有一定的开销,同时每次更新属性都需要把全部数据取出进行反序列化,更新后再序列化到Redis中。

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

    1. 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进行存储的时候

新增或更新一个配置项

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

获取一个配置项

  1. 127.0.0.1:6379> HGET 10001 AppName
  2. "myblog"

删除一个配置项

  1. 127.0.0.1:6379> HDEL 10001 AppName
  2. (integer) 1

3.2 购物车

    很多电商网站都会使用 cookie实现购物车,也就是将整个购物车都存储到 cookie里面。这种做法的一大优点:无须对数据库进行写入就可以实现购物车功能,这种方式大大提高了购物车的性能,而缺点则是程序需要重新解析和验证( validate) cookie,确保cookie的格式正确,并且包含的商品都是真正可购买的商品。cookie购物车还有一个缺点:因为浏览器每次发送请求都会连 cookie一起发送,所以如果购物车cookie的体积比较大,那么请求发送和处理的速度可能会有所降低。

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

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

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

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

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

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

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

3.3 计数器

    Redis 哈希表作为计数器的使用也非常广泛。它常常被用在记录网站每一天、一月、一年的访问数量。每一次访问,我们在对应的field上自增1

  1. //记录我的
  2. 127.0.0.1:6379> HINCRBY MyBlog 202001 1
  3. (integer) 1
  4. 127.0.0.1:6379> HINCRBY MyBlog 202001 1
  5. (integer) 2
  6. 127.0.0.1:6379> HINCRBY MyBlog 202002 1
  7. (integer) 1
  8. 127.0.0.1:6379> HINCRBY MyBlog 202002 1
  9. (integer) 2

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

  1. 127.0.0.1:6379> HINCRBY pid:1 Good 1
  2. (integer) 1
  3. 127.0.0.1:6379> HINCRBY pid:1 Good 1
  4. (integer) 2
  5. 127.0.0.1:6379> HINCRBY pid:1 bad 1
  6. (integer) 1

也可以实时记录当天的在线的人数。

  1. //有人登陆
  2. 127.0.0.1:6379> HINCRBY MySite 20200310 1
  3. (integer) 1
  4. //有人登陆
  5. 127.0.0.1:6379> HINCRBY MySite 20200310 1
  6. (integer) 2
  7. //有人登出
  8. 127.0.0.1:6379> HINCRBY MySite 20200310 -1
  9. (integer) 1

小结

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

参考

《Redis设计与实现》

《Redis开发与运维》

《Redis官方文档》

-----END-----

Redis对象——哈希(Hash)的更多相关文章

  1. Redis中的哈希(Hash)

    Redis 哈希(Hash) Redis hash 是一个string类型的field和value的映射表,hash特别适合用于存储对象. Redis 中每个 hash 可以存储 232 - 1 键值 ...

  2. Redis 命令,键(key),字符串(String),哈希(Hash),列表(List),集合(Set)(二)

      Redis 命令 Redis 命令用于在 redis 服务上执行操作. 要在 redis 服务上执行命令需要一个 redis 客户端.Redis 客户端在我们之前下载的的 redis 的安装包中. ...

  3. redis(八):Redis 哈希(Hash)

    Redis 哈希(Hash) Redis hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象. Redis 中每个 hash 可以存储 232 ...

  4. Python操作redis系列以 哈希(Hash)命令详解(四)

    # -*- coding: utf-8 -*- import redis #这个redis不能用,请根据自己的需要修改 r =redis.Redis(host=") 1. Hset 命令用于 ...

  5. redis 哈希(hash)函数

    哈希(hash)函数 hSet 命令/方法/函数 Adds a value to the hash stored at key. If this value is already in the has ...

  6. 【redis源码阅读】redis对象

    结构定义 在redis中,对象的数据结构定义如下: ​typedef struct redisObject { ​unsigned type:4; ​unsgined encoding:4; ​uns ...

  7. redist命令操作(二)--哈希Hash,列表List

    1.Redis 哈希(Hash) 参考菜鸟教程:http://www.runoob.com/redis/redis-hashes.html Redis hash 是一个string类型的field和v ...

  8. 一致性哈希(hash)算法

    一.算法背景 一致性哈希算法在1997年由麻省理工学院的Karger等人在解决分布式Cache中提出的,设计目标是为了解决因特网中的热点(Hot spot)问题,初衷和CARP十分类似.一致性哈希修正 ...

  9. Redis对象类型

    Redis对象类型 Redis基于基础的数据结构创建的对象: 字符串对象. 列表对象. 哈希对象. 集合对象 有序集合对象. 对象回收:Redis对象系统实现了基于引用计数技术的内存回收机制,当程序不 ...

随机推荐

  1. H5页面通用头部设置

    见到很多人写H5页面都不设置头部,不忍直视,于是整理一篇文章,不定期更新,为了让自己显得专业一点,也为了方便自己复制粘贴 一般来说必须设置项 <!-- 页面编码 --> <meta ...

  2. vue+express+mysql项目总结(node项目部署阿里云通用)

    原文发布于我的个人博客上:原文点这里   前面经历千辛万苦,终于把博客的所有东西都准备好了,现在就只等部署了.下面我介绍下我的部署过程: 一.购买服务器和域名   如果需要域名(不用域名通过ip也可以 ...

  3. notepad++ 字符处理: 字符前后删除 或 删除未包含字符串的行

    字符串前后删除 删除str之后的所有字符用,打开替换(Ctrl+H) :str.*$ 删除str之前的所有字符用:^.*str 如果是其他字符就把str替换为其他字符 ---------------- ...

  4. PHP sprintf() 函数详解

    PHP中,sprintf()的作用是把字符串进行多种类型的格式化一般用法如下: sprintf ( string $format [, mixed $... ] ) : string 返回一个按要求格 ...

  5. php获取当前周的第一天与最后一天

    1 2 3 4 5 6 7 8 9 10 // 当前日期   $sdefaultDate = date("Y-m-d");   // $first =1 表示每周星期一为开始日期  ...

  6. vue中v-if和v-show的区别

    v-if.v-show顾名思义就是用来判断视图层展示效果的.  v-if 指令用于条件性地渲染一块内容.这块内容只会在指令的表达式返回真值的时候被渲染. v-show 指的是单纯的切换元素的样式dis ...

  7. LoadRunner 11破解方法:

    LoadRunner 11破解方法: 请严格安装顺序操作! a.用LR8.0中的mlr5lprg.dll.lm70.dll覆盖LR11安装目录下“bin”文件夹中的对应文件: b.运行deleteli ...

  8. php实现下载功能

    <?php header("Content-type:text/html;charset=utf-8"); $file_name="1.text"; // ...

  9. cooke和session

    一.装饰器要加入funtools.wrap装饰 保留函数的元数据(函数名/注释) 1.装饰器 def wrapper(f): def inner(*args,**kwargs): return f(* ...

  10. vue之冒泡阻止

    用了Element ui写页面 <el-dropdown-menu slot="dropdown"> <el-dropdown-item> <el-s ...