Redis:09---Hash对象


一、哈希对象简介

  • 几乎所有的编程语言都提供了哈希(hash)类型,它们的叫法可能是哈希、字典、关联数组

  • 哈希又称散列

  • 在Redis中,哈希类型是指键值本身又是一个键值对结构,形如value={{field1,value1},...{fieldN,valueN}},Redis键值对和哈希类型二者的关系可以下图表示

  • 一些特点:

    • 存储多个键值对之间的映射,并且键值对不允许重复

    • 在某一个固定的key中,其对应value中的field也不允许重复

    • 散列存储的值既可以是字符串也可以是数字值

    • 用户同样可以对散列存储的数字值执行自增操作或自减操作

    • 散列在很多方面是一个微缩版的Redis,不少字符串命令都有相应的散列版本

    • 熟悉文档数据库的读者可以将散列看作是文档数据库里面的文档,而熟悉关系数据库的读者可以将散列看作是关系数据库里面的行。因为“文档、行、散列”这三者都允许用户同时访问或修改一个或多个域

  • 注意:哈希类型中的映射关系叫作field-value,注意这里的value是指field对应的值,不是键对应的值,请注意value在不同上下文的作用。

二、命令

常用命令

  • hset:设置值。如果设置成功会返回1,反之会返回0

hset key field value

      hsetnx:它们的关系就像set和setnx命令一样,只不过作用域由键变为field

       

  • hget:获取值。如果键或field不存在,返回nil

hget key field

好,到这里会有人好奇,这里到底是怎么样的结构,能不能直观的看到这些记录,还是我在前几篇文章说到的Redis DeskTop Manager可以查看

  • hdel:删除field

    • hdel会删除一个或多个field,返回结果为成功删除field的个数

    • 直到某一个key对应的field全部删除完全之后,该哈希对象才会被删除

hdel key field [field ...]

  • hlen:计算fileld个数

hlen key

  • hmget、hmset:批量获取/设置field-value

hmget key field [field ...]
hmset key field value [field value ...]

  • hstrlen:计算value的字符串长度(需要Redis3.2以上)

hstrlen key field

其他命令

  • hincrby、hincrbyfloat:hincrby和hincrbyfloat,就像incrby和incrbyfloat命令一样,但是它们的作用域是filed

  • hexists:判断field是否存在。field存在返回1,不包含返回0

  • hkeys:获取所有field

hkeys key

  • hvals:获取所有值

hvals key
  • hgetall:获取所有的field-value

hgetall key

  • 提示:在使用hgetall时,如果哈希元素个数比较多,会存在阻塞Redis的可能。 如果开发人员只需要获取部分field,可以使用hmget,如果一定要获取全部 field-value,可以使用hscan命令,该命令会渐进式遍历哈希类型.

  • 下图给出了哈希类型命令的时间复杂度:

三、内部编码

  • 哈希类型的内部编码有两种:

    • ziplist(压缩列表):当哈希类型元素个数小于hash-max-ziplist-entries 配置(默认512个)、同时所有值都小于hash-max-ziplist-value配置(默认64 字节)时,Redis会使用ziplist作为哈希的内部实现,ziplist使用更加紧凑的 结构实现多个元素的连续存储,所以在节省内存方面比hashtable更加优秀

    • hashtable(哈希表):当哈希类型无法满足ziplist的条件时,Redis会使 用hashtable作为哈希的内部实现,因为此时ziplist的读写效率会下降,而 hashtable的读写时间复杂度为O(1)

演示说明

  • 当field个数比较少且没有大的value时,内部编码为ziplist:

  • 当有value大于64字节,内部编码会由ziplist变为hashtable:

  • 当field个数超过512,内部编码也会由ziplist变为hashtable

四、字符串和散列的比较与选择

散列的优点

  • 散列的最大优势,只需要在数据库里面创建一个键,就可以把任意多的字段和值存储到散列里面

字符串的优点

  • 虽然散列键命令和字符串键命令在部分功能上有重合的地方,但是字符串键命令提供的操作比散列键命令更为丰富。比如,字符串能够使用 SETRANGE 命令和 GETRANGE 命令设置或者读取字符 串值的其中一部分,或者使用 APPEND 命令将新内容追加到字符串值的末尾,而散列键并不支持 这些操作

  • 再比如我们要设置键过期时间,键过期时间是针对整个键的,用户无法为散列中的不同字段设置不 同的过期时间,所以当一个散列键过期的时候,他包含的所有字段和值都会被删除。与此相反,如 果用户使用字符串键存储信息项,就不会遇到这样的问题——用户可以为每个字符串键分别设置不 同的过期时间,让它们根据实际的需要自动被删除

字符串和散列的选择

  • 使用场景对比:

    • 如果程序需要为单个数据项单独设置过期的时间,那么使用字符串键。

    • 如果程序需要对数据项执行诸如 SETRANGE、GETRANGE 或者 APPEND 等操作,那么优 先考虑使用字符串键。当然,用户也可以选择把数据存储在散列中,然后将类似 SETRANG E、GETRANGE 这样的操作交给客户端执行

    • 如果程序需要存储的数据项比较多,并且你希望尽可能地减少存储数据所需的内存,就应该优 先考虑使用散列键

    • 如果多个数据项在逻辑上属于同一组或者同一类,那么应该优先考虑使用散列键

五、使用场景

短网址生成程序

  • 此时我们可以根据该短链接查询到具体的源网址,并记录点击次数

存储信息

  • 下图为关系型数据表记录的两条用户信息,用户的属性作为表的列, 每条用户信息作为行

  • 如果将其用哈希类型存储,如下图所示:

  • 相比于使用字符串序列化缓存用户信息,哈希类型变得更加直观,并且在更新操作上会更加便捷。可以将每个用户的id定义为键后缀,多对fieldvalue对应每个用户的属性,类似如下伪代码:

UserInfo getUserInfo(long id){// 用户id作为key后缀userRedisKey = "user:info:" + id;// 使用hgetall获取所有用户信息映射关系userInfoMap = redis.hgetAll(userRedisKey);UserInfo userInfo;if (userInfoMap != null) {// 将映射关系转换为UserInfouserInfo = transferMapToUserInfo(userInfoMap);} else {// 从MySQL中获取用户信息userInfo = mysql.get(id);// 将userInfo变为映射关系使用hmset保存到Redis中redis.hmset(userRedisKey, transferUserInfoToMap(userInfo));// 添加过期时间redis.expire(userRedisKey, 3600);}return userInfo;}
  • 但是需要注意的是哈希类型和关系型数据库有两点不同之处:

    • 哈希类型是稀疏的,而关系型数据库是完全结构化的,例如哈希类型 每个键可以有不同的field,而关系型数据库一旦添加新的列,所有行都要为 其设置值(即使为NULL),如下图所示

    • 关系型数据库可以做复杂的关系查询,而Redis去模拟关系型复杂查询 开发困难,维护成本高

三种方案

  • 开发人员需要将两者的特点搞清楚,才能在适合的场景使用适合的技术。到目前为止,我们已经能够用三种方法缓存用户信息,下面给出三种方案的实现方法和优缺点分析

  • ①原生字符串类型:每个属性一个键

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

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

set user:1:name tom
set user:1:age 23
set user:1:city beijin
  • ②序列化字符串类型:将用户信息序列化后用一个键保存。

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

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

set user:1 serialize(userInfo)
  • ③哈希类型:每个用户属性使用一对field-value,但是只用一个键保存

    • 优点:简单直观,如果使用合理可以减少内存空间的使用

    • 缺点:要控制哈希在ziplist和hashtable两种内部编码的转换,hashtable会消耗更多内存

hmset user:1 name tomage 23 city beijing

不过对于我而言,我经常用到的是string,方便,快捷,一般是存储一些玩家的登录缓存信息,或者一些全服跨服战斗的相关数据

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/444459.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

leetcode329. 矩阵中的最长递增路径

给定一个整数矩阵,找出最长递增路径的长度。 对于每个单元格,你可以往上,下,左,右四个方向移动。 你不能在对角线方向上移动或移动到边界外(即不允许环绕)。 示例 1: 输入: nums [ [9,9,…

Query Ajax 实例 ($.ajax、$.post、$.get)

Jquery在异步提交方面封装的很好,直接用AJAX非常麻烦,Jquery大大简化了我们的操作,不用考虑浏览器的诧异了。 推荐一篇不错的jQuery Ajax 实例文章,忘记了可以去看看,地址为:http://www.cnblogs.com/yeer/a…

C++:18---const关键字(附常量指针、指针常量、常量指针常量)

一、const变量的一些基本特点 ①const修饰的变量不能被修改const int a=10; a=20;//错误②因为const修饰的变量不能被修改,所以必须被初始化int a=10; const int b=a; //正确 const int c=10; //正确③const修饰的变量可以赋值给其他值const int a=10; int b=a;//正确④可以有…

leetcode330. 按要求补齐数组 顶级难度玄学贪心

给定一个已排序的正整数数组 nums,和一个正整数 n 。从 [1, n] 区间内选取任意个数字补充到 nums 中,使得 [1, n] 区间内的任何数字都可以用 nums 中某几个数字的和来表示。请输出满足上述要求的最少需要补充的数字个数。 示例 1: 输入: nums [1,3], …

(二十)TCPIP面试宝典-进入大厂必备总结(中)

TCP 作为传输层的协议,是一个IT工程师素养的体现,也是面试中经常被问到的知识点。在此,我将 TCP 核心的一些问题梳理了一下,希望能帮到各位。 实际上这篇文章相当于是复习之前的网络基础部分。只不过这篇文章的提问方式更灵活,也是让读者们懂得变通,更熟悉TCP。 前两篇文…

(二十一)TCPIP面试宝典-进入大厂必备总结(下)

TCP 作为传输层的协议,是一个IT工程师素养的体现,也是面试中经常被问到的知识点。在此,我将 TCP 核心的一些问题梳理了一下,希望能帮到各位。 实际上这篇文章相当于是复习之前的网络基础部分。只不过这篇文章的提问方式更灵活,也是让读者们懂得变通,更熟悉TCP。 上一篇文…

C:03---运算符优先级

二话不说先看运算符的优先级表: 一、逗号运算符 格式:整个逗号表达式的值返回的结果是最后一个表达式的值使用起来,最好加上括号来返回最后一个表达式的值。否则逗号表达式的意义将失效(见下面演示案例)(表达式1, 表达式2, 表达式3....); #include <stdio.h> int ma…

(二十二)深入浅出TCPIP之实战篇—用c++开发一个http服务器

在当前的网络编程专栏前十几篇文章里&#xff0c;我已经说明了TCPIP常用的一些原理&#xff0c;那么接下来我将逐步进入到实战编程阶段&#xff1a;本篇文章我将带大家用C做一个http服务器。既然想实现一个http服务器&#xff0c;首先必须要熟悉的就是http协议知识&#xff0c;…

leetcode161. 相隔为 1 的编辑距离

给定两个字符串 s 和 t&#xff0c;判断他们的编辑距离是否为 1。 注意&#xff1a; 满足编辑距离等于 1 有三种可能的情形&#xff1a; 往 s 中插入一个字符得到 t 从 s 中删除一个字符得到 t 在 s 中替换一个字符得到 t 示例 1&#xff1a; 输入: s "ab", t …

leetcode164. 最大间距 借桶思想秒掉hard题

给定一个无序的数组&#xff0c;找出数组在排序之后&#xff0c;相邻元素之间最大的差值。 如果数组元素个数小于 2&#xff0c;则返回 0。 示例 1: 输入: [3,6,9,1] 输出: 3 解释: 排序后的数组是 [1,3,6,9], 其中相邻元素 (3,6) 和 (6,9) 之间都存在最大差值 3。 示例 2: …

Log4j使用总结

一、介绍Log4j是Apache的一个开放源代码项目&#xff0c;通过使用Log4j&#xff0c;我们可以控制日志信息输送的目的地是控制台、文件、GUI组件、甚至是套接口服务 器、NT的事件记录器、UNIX Syslog守护进程等&#xff1b;我们也可以控制每一条日志的输出格式&#xff1b;通过定…

C语言: GDB调试技术(一)

启动GDB的方法有以下几种: 1、gdb <program> program也就是你的执行文件,一般在当然目录下。’ 例如我写了一个简单的helloword程序 #include <stdio.h> int main(){int a = 1;char* ch = "hello world";printf("%s\n",ch);return 0; }那么我…

leetcode52. N皇后 II 最强解法直接秒杀100%

n 皇后问题研究的是如何将 n 个皇后放置在 nn 的棋盘上&#xff0c;并且使皇后彼此之间不能相互攻击。 上图为 8 皇后问题的一种解法。 给定一个整数 n&#xff0c;返回 n 皇后不同的解决方案的数量。 示例: 输入: 4 输出: 2 解释: 4 皇后问题存在如下两个不同的解法。 [ […

leetcode145. 二叉树的后序遍历 意想不到的骚操作

给定一个二叉树&#xff0c;返回它的 后序 遍历。 示例: 输入: [1,null,2,3] 1 \ 2 / 3 输出: [3,2,1] 进阶: 递归算法很简单&#xff0c;你可以通过迭代算法完成吗&#xff1f; 思路&#xff1a;前序遍历左右交换&#xff0c;然后倒序输出 原因&am…

C++:29 --- C++继承关系下的内存布局(下)

1 单继承 C++ 提供继承的目的是在不同的类型之间提取共性。比如,科学家对物种进行分类,从而有种、属、纲等说法。有了这种层次结构,我们才可能将某些具备特定性质的东西归入到最合适的分类层次上,如“怀孩子的是哺乳动物”。由于这些属性可以被子类继承,所以,我们只要知道…

leetcode119. 杨辉三角 II 你能比我代码更短吗?

给定一个非负索引 k&#xff0c;其中 k ≤ 33&#xff0c;返回杨辉三角的第 k 行。 示例: 输入: 3 输出: [1,3,3,1]按照定义写即可。 class Solution:def getRow(self, rowIndex: int) -> List[int]:l[1]for i in range(rowIndex):l[1][l[j]l[j1] for j in range(len(l)-1…

C++:28 --- C++内存布局(上)

了解你所使用的编程语言究竟是如何实现的,对于C++程序员可能特别有意义。 首先,我们顺次考察C兼容的结构(struct)的布局,单继承,多重继承,以及虚继承;接着,我们讲成员变量和成员函数的访问,当然,这里面包含虚函数的情况;再接下来,我们考察构造函数,析构函数,以…

使用Log4j为项目配置日志输出应用详细总结及示例演示.

Log4j组件构成 Log4j由三个重要的组件构成&#xff1a; 1.日志信息的优先级(Logger) 2.日志信息的输出目的地(Appender) 3.日志信息的输出格式(Layout)。 概要: 日志信息的优先级从高到低有ERROR、WARN、INFO、DEBUG&#xff0c;分别用来指定这条日志信息的重要程度&…

leetcode205. 同构字符串 一般人一次做不对的简单题

给定两个字符串 s 和 t&#xff0c;判断它们是否是同构的。 如果 s 中的字符可以被替换得到 t &#xff0c;那么这两个字符串是同构的。 所有出现的字符都必须用另一个字符替换&#xff0c;同时保留字符的顺序。两个字符不能映射到同一个字符上&#xff0c;但字符可以映射自己…

C++:32---IO库

一、IO库 I0库类型和头文件头文件类型iostreamistream,wistream从流读取数据ostream,wostream向流写入数据iostream,wiostream读写流fstreamifstream,wifstream从文件读取数据ofstream,wofstream向文件写入数据fstream,wfstream读写文件sstreamistringstream,wistringst