Redis(二):常见数据类型:String 和 哈希

news2024/11/19 20:24:04

引言

Redis 提供了 5 种数据结构,理解每种数据结构的特点对于 Redis 开发运维⾮常重要,同时掌握每
种数据结构的常⻅命令,会在使⽤ Redis 的时候做到游刃有余。
Redis 的命令有上百种,我们不可能全部死记硬背下来,但是如果理解了 Redis 的以下机制,会发现这些命令有很强的通用性。
Redis 并不是万金油,有些数据结构和命令必须在特定的场景下使用,否则使用不当会对 Redis 本身或者应用本身造成致命伤害。

基本全局命令

Redis的key的操作命令如图所示:

语法  功能
set key value把key和value存储进去 
get key根据key来去取value
keys pattern    查看数据库所有符合pattern的key
exists key [key…]判断key是否存在
expire key seconds设置 key 的生存时间,超过时间,key 自动删除。单位是秒。
ttl key以秒为单位,返回 key 的剩余生存时间(ttl: time to live)
type key 查看 key 所存储值的数据类型
del key [key…] 删除存在的 key,不存在的 key 忽略。

set

set key value:把key和value存储进去

例如:set key1 1

get

get key : 根据key来去取value

例如:get key1

KEYS

keys pattern:用来查看匹配规则的key

具体的匹配规则表示如下:

  1. keys   h?llo :  “?”表示匹配任意一个字符
  2. keys   h*llo  :   “*”表示匹配0个或者多个任意字符
  3. keys   h[abcd]llo :    "[]"表示只能匹配内部写的固定格式
  4. keys   h[^e]llo   :  "^e"表示只排除e,其他全部匹配
  5. keys   h[a-e]llo  :   "a-e"表示只匹配 a-e 之间的包括e和e

该命令的时间复杂度为:O(N),因为他需要遍历所有的 key,返回值为:所有匹配的 key

EXISTS

exists key [key…]:   判断key是否存在

例如:

时间复杂度为:O(N),官方文档上写的是O(N),事实上这里的 N 是你写了几个。

其实我们大可以不用一个个去查询,我们可以将多个key 一起查询,时间复杂度的 N 就是这里查询几个,查两个就是O(2)例如:

del

del是 delete 的缩写,

del key [key…]:删除存在的 key,不存在的 key 忽略。

时间复杂度为:O(N),这个 N 指的和上述一样,同样它们都是可能一次删除多个 key;

返回值为:删除成功的个数,key 不存在即:返回 0

例如:

EXPIRE

expire:  给指定的 key 设置过期时间(key 存活时间超出这个指定的值,就过期了)单位是秒

举例:expire key (seconds) 单位:秒

举例:pexipire key 单位:毫秒

来看看实际操作:

设置一个key,设置一个过期时间,一旦到了过期时间键值对就自动删除了。

TTL

TTL key:以秒为单位,返回 key 的剩余生存时间(ttl: time to live)

时间复杂度:O(1)
返回值:剩余过期时间。-1 表⽰没有关联过期时间,-2 表⽰ key 不存在。

图二,我们并没有关联过期时间,就会返回 -1

TYPE

type key:查看 key 所存储值的数据类型

具体的类型这里先不讨论。

这些就是最最基本的几个命令了,没事的时候可以多练,熟能生巧。

这里只是抛砖引玉,为后面五种数据结构的介绍做一个热身。

Redis 的五种数据结构

五种数据结构如图所示:

简易描述:

  1. 字符串:Redis的字符串数据结构是由SDS(Simple Dynamic String,简单动态字符串)实现的。SDS是Redis中所有非数字键的底层实现。
  2. 列表:Redis的列表数据结构是由双向链表实现的。这个链表在内存中分为两部分,一部分是“统筹部分”,指向链表的头部和尾部,以及链表的长度;另一部分是“具体实施方”,是一目了然的双向链表结构,每个节点都有前驱和后继。
  3. 哈希:Redis的哈希数据结构底层是由字典实现的。在Redis中,哈希表被称为“字典”,其元素是键值对。
  4. 集合:Redis的集合数据结构底层是由哈希表实现的。集合在Redis中被称为“set”,其元素是无序的,且不允许重复。
  5. 有序集合:Redis的有序集合数据结构底层也是由哈希表实现的。有序集合在Redis中被称为“zset”,其元素是唯一的,但可以重复。每个元素都有一个相关的分数,这个分数用于在有序集合中按分数从小到大排序元素。
实际上 Redis 针对每种数据结构都有⾃⼰的底层内部编码实现,⽽且是多种实现,这样 Redis 会
在合适的场景选择合适的内部编码;

Redis 数据结构及内部编码:

可以看到每种数据结构都有⾄少两种以上的内部编码实现,例如 list 数据结构包含了 linkedlist 和 ziplist 两种内部编码。同时有些内部编码,例如 ziplist,可以作为多种数据结构的内部实现,可以通 过 object encoding 命令查询内部编码:

例如:

可以看到 hello 对应值的内部编码是 embstr,键 mylist 对应值的内部编码是 ziplist。
Redis 这样设计有两个好处:
  1. 可以改进内部编码,⽽对外的数据结构和命令没有任何影响,这样⼀旦开发出更优秀的内部编码,⽆需改动外部数据结构和命令,例如 Redis 3.2 提供了 quicklist,结合了 ziplist 和 linkedlist 两者的优势,为列表类型提供了⼀种更为优秀的内部编码实现,⽽对⽤⼾来说基本⽆感知
  2. 多种内部编码实现可以在不同场景下发挥各⾃的优势,例如 ziplist ⽐较节省内存,但是在列表元素⽐较多的情况下,性能会下降,这时候 Redis 会根据配置选项将列表类型的内部实现转换为 linkedlist,整个过程⽤⼾同样⽆感知。

String 类型

key都是固定的字符串,value实际上会有多种类型

redis 中的字符串,直接按照二进制数据的方式存储的(不做任何编码转换,存的是啥,取出来就是啥):可以是文本、整数、JSON、xml、二进制数据(图片、视频、音频)

String 类型中的关键命令

命令如下:

  1. SET key value:设置指定键的值为指定字符串。
  2. GET key:获取指定键对应的值。
  3. APPEND key value:将指定字符串追加到指定键的值末尾。
  4. INCR key:将指定键的值加1。
  5. DECR key:将指定键的值减1。
  6. INCRBY key increment:将指定键的值加上指定的整数。
  7. DECRBY key decrement:将指定键的值减去指定的整数。
  8. STRLEN key:获取指定键值的长度。
  9. SETNX key value:如果指定键不存在,则设置键的值为指定字符串。
  10. MSET key1 value1 key2 value2 ...:设置多个键值对。
  11. MGET key1 key2 ...:获取多个键值对的值。
  12. GETSET key value:先获取指定键的值,然后设置键的值为指定字符串。
  13. SETEX key seconds value:设置键的值为指定字符串,并设定生存时间(单位为秒)。
  14. PSETEX key milliseconds value:设置键的值为指定字符串,并设定生存时间(单位为毫秒)。
  15. MSETNX key1 value1 key2 value2 ...:如果所有指定的键都不存在,则设置多个键值对。
  16. STRALGO LCP key1 key2 ...:计算多个字符串的最长公共前缀。

这里就不一一举例了,我就简单介绍几个重点的,看不懂的可以去看一看官方文档:

Redis官方文档

在这里查询 String 类型即可,很多时候,我们还是得学会自己查看文档,未来到了公司也是一样,不可能永远都有中文文档查看的;不懂得单词使用翻译软件查一下。

我这里就简单介绍几个:

SET:

SET key value [expiration EX seconds|PX milliseconds] [NX|XX]
SET 命令⽀持多种选项来影响它的⾏为:
  • EX seconds⸺使⽤秒作为单位设置 key 的过期时间。
  • PX milliseconds⸺使⽤毫秒作为单位设置 key 的过期时间。
  • NX ⸺只在 key 不存在时才进⾏设置,即如果 key 之前已经存在,设置不执⾏。
  • XX ⸺只在 key 存在时才进⾏设置,即如果 key 之前不存在,设置不执⾏。
注意:由于带选项的 SET 命令可以被 SETNX SETEX PSETEX 等命令代替,所以之后的版本中,Redis 可能进⾏合并。

返回值:

  • 如果设置成功,返回 OK。
  • 如果由于 SET 指定了 NX 或者 XX 但条件不满⾜,SET 不会执⾏,并返回 (nil)。

如图:

MSET

一次性设置多个 key 的值

语法:

MSET key value [key value ...]

keys * 就是查询所有的 键,“ * ” 表示一个通配符。

很多正常使用的时候并非一个键一个键去操作,那么效率会非常低,这时候:MSET 就排上大用场了。

有 mset 也就有 mget,一次获取多个键

MGET

一次性处理多个get

这个命令,现在过过瘾就好,工作中千万不要使用,公司里,可能有非常非常非常非常多的键,使用一次这个命令,十分有可能将公司的机器给干崩               

就像是 MySQL 中的 select * 命令。

内部编码

字符串类型的内部编码有三种:

  • int:8 个字节的⻓整型。
  • embstr:⼩于等于 39 个字节的字符串。
  • raw:⼤于 39 个字节的字符串。

redis 会根据当前值的类型和长度动态决定使用哪种内部编码实现的。

具体这么确定使用哪个呢?

字符串长度小于等于 39 个字节的字符串使用 “ embstr ”,大于 39 个字节的字符使用 “  raw ”;

如果在面试中,千万不要说 39 这个数字,事实上在不同的业务场景都是不同的(解释不清楚39是哪来的)

具体如何解决?
    1. 先看 redis 是否提供了相应的配置文件,在配置文件中修改
    2. 如果没有提供,就需要对 redis 的源码进行魔改

String典型使用场景

缓存(Cache)功能
Redis + MySQL 组成的缓存存储架构

这是比较经典的缓存使用场景,其中 Redis 作为缓冲层,MySQL 作为存储层,绝大部分请求的数据都是从 Redis 中获取,由于 Redis 具有支撑高并发的特性,所以缓存通常能起到加速读写和降低后端压力的作用。

大致流程:

客户端每次读取请求都发送给 Redis,如果 Redis 的 key 有值就直接返回给客户端;如果 Redis 没有这个 key ,再由 Redis 将请求发送给 MySQL ,MySQL 返回的值将保留在 Redis 中。

计数(Counter)功能

许多应⽤都会使⽤ Redis 作为计数的基础⼯具,它可以实现快速计数、查询缓存的功能,同时数 据可以异步处理或者落地到其他数据源。例如视频⽹站的视频播放次数可以使⽤ Redis 来完成:⽤⼾每播放⼀次视频,相应的视频播放数就会⾃增 1。
 
实际中要开发⼀个成熟、稳定的真实计数系统,要⾯临的挑战远不⽌如此简单:防作弊、按
照不同维度计数、避免单点问题、数据持久化到底层数据源等。

共享会话(Session)

⼀个分布式 Web 服务将⽤⼾的 Session 信息(例如⽤⼾登录信息)保存在各⾃ 的服务器中,但这样会造成⼀个问题:出于负载均衡的考虑,分布式服务会将⽤⼾的访问请求均衡到 不同的服务器上,并且通常⽆法保证⽤⼾每次请求都会被均衡到同⼀台服务器上,这样当⽤⼾刷新⼀ 次访问是可能会发现需要重新登录,这个问题是⽤⼾⽆法容忍的。
为了解决这个问题,可以使⽤ Redis 将⽤⼾的 Session 信息进⾏集中管理,在这种模式下,只要保证 Redis 是⾼可⽤和可扩展性的,⽆论⽤⼾被均衡到哪台 Web 服务器上,都集中从 Redis 中查询、更新 Session 信息

如上图所示。

手机验证码

很多应⽤出于安全考虑,会在每次进⾏登录时,让⽤⼾输⼊⼿机号并且配合给⼿机发送验证码, 然后让⽤⼾再次输⼊收到的验证码并进⾏验证,从⽽确定是否是⽤⼾本⼈。为了短信接⼝不会频繁访 ,会限制⽤⼾每分钟获取验证码的频率,例如⼀分钟不能超过 5 次
String 发送验证码(phoneNumber) {
 	key = "shortMsg:limit:" + phoneNumber;
 	// 设置过期时间为 1 分钟(60 秒)
	// 使⽤ NX,只在不存在 key 时才能设置成功
 	bool r = Redis 执⾏命令:set key 1 ex 60 nx
 	if (r == false) {
 		// 说明之前设置过该⼿机的验证码了
 		long c = Redis 执⾏命令:incr key
 		if (c > 5) {
 			// 说明超过了⼀分钟 5 次的限制了
 			// 限制发送
 			return null;
		 }
 	}
 
 	// 说明要么之前没有设置过⼿机的验证码;要么次数没有超过 5 次
	 String validationCode = ⽣成随机的 6 位数的验证码();
 
 	validationKey = "validation:" + phoneNumber;
 	// 验证码 5 分钟(300 秒)内有效
	 Redis 执⾏命令:set validationKey validationCode ex 300;
 
	 // 返回验证码,随后通过⼿机短信发送给⽤⼾
 	return validationCode ;
	}
// 验证⽤⼾输⼊的验证码是否正确
bool 验证验证码(phoneNumber, validationCode) {
 	validationKey = "validation:" + phoneNumber;
 
 	String value = Redis 执⾏命令:get validationKey;
 	if (value == null) {
 		// 说明没有这个⼿机的验证码记录,验证失败
 		return false;
 	}
 
	 if (value == validationCode) {
		 return true;
 	} else {
 		return false;
 	}
}

哈希类型

所谓的哈希类型就是数据结构中的哈希表,只是这里又有些不同。

在 Redis 中,哈希类型是指值本⾝⼜是⼀个键值对结构,形如 key = "key",value = { {
field1, value1 }, ..., {fieldN, valueN } }
哈希类型中的映射关系通常称为 field-value,⽤于区分 Redis 整体的键值对(key-value),
注意这⾥的 value 是指 field 对应的值,不是键(key)对应的值,请注意 value 在不同上下
⽂的作⽤。

哈希类型中的关键命令

关键命令如下:

  1. HSET:向哈希表中添加一个字段和值。
  2. HGET:从哈希表中读取一个字段的值。
  3. HGETALL:从哈希表中读取所有字段和值。
  4. HDEL:从哈希表中删除一个或多个字段。
  5. HEXISTS:检查哈希表中是否存在指定的字段。
  6. HKEYS:获取哈希表中所有字段的名称。
  7. HVALS:获取哈希表中所有字段的值。
  8. HLEN:获取哈希表中字段的数量。
  9. HMSET:向哈希表中添加多个字段和值。
  10. HMGET:从哈希表中读取多个字段的值。

同样的这里也就不再一一介绍了,参考下列官方:

哈希类型的官方文档

内部编码

哈希的内部编码有如下两种:

  • ziplist(压缩列表):当哈希类型元素个数⼩于 hash-max-ziplist-entries 配置(默认 512 个)、 同时所有值都⼩于 hash-max-ziplist-value 配置(默认 64 字节)时,Redis 会使⽤ ziplist 作为哈 希的内部实现,ziplist 使⽤更加紧凑的结构实现多个元素的连续存储,所以在节省内存⽅⾯⽐ hashtable 更加优秀。
  • hashtable(哈希表):当哈希类型⽆法满⾜ ziplist 的条件时,Redis 会使⽤ hashtable 作为哈希 的内部实现,因为此时 ziplist 的读写效率会下降,⽽ hashtable 的读写时间复杂度为 O(1)。

举例:

当 field 个数超过 512 时,内部编码也会转换为 hashtable:
这里就懒得一个个添加了
不是说,这几个数字就唯一了,我们也可以通过配置文件去修改:

配置

  • 可以在 redis.conf 文件中修改配置项 : hash-max-ziplist-entries 配置(默认512个)【当前哈希表中的元素超过多少,可以转换为 hashtable】
  • 同样可以修改: hash-max-ziplist-value(默认64字节)【当前哈希表中的元素长度超过多少,可以转换为 hashtable】

使用场景

String 适合做缓存,相比于使用相⽐于使⽤ JSON 格式的字符串缓存⽤⼾信息,哈希类型变得更加直观,并且在更新操作上变得更灵活。

但是需要注意的是哈希类型和关系型数据库有两点不同之处:
  • 哈希类型是稀疏的,⽽关系型数据库是完全结构化的,例如哈希类型每个键可以有不同的 field,⽽关系型数据库⼀旦添加新的列,所有⾏都要为其设置值,即使为 null。
  • 关系数据库可以做复杂的关系查询,⽽ Redis 去模拟关系型复杂查询,例如联表查询、聚合查询等基本不可能,维护成本⾼。

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

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

相关文章

9.2 Windows驱动开发:内核解析PE结构导出表

在笔者的上一篇文章《内核特征码扫描PE代码段》中LyShark带大家通过封装好的LySharkToolsUtilKernelBase函数实现了动态获取内核模块基址,并通过ntimage.h头文件中提供的系列函数解析了指定内核模块的PE节表参数,本章将继续延申这个话题,实现…

vue实现动态路由菜单!!!

目录 总结一、步骤1.编写静态路由编写router.jsmain.js注册 2.编写permisstions.js权限文件编写permisstions.jsaxios封装的APIstore.js状态库system.js Axios-APIrequest.js axios请求实例封装 3.编写菜单树组件MenuTree.vue 4.主页中使用菜单树组件 总结 递归处理后端响应的…

java基础-IO

1、基础概念 1.1、文件(File) 文件的读写可以说是开发中必不可少的部分,因为系统会存在大量处理设备上的数据,这里的设备指硬盘,内存,键盘录入,网络传输等。当然这里需要考虑的问题不仅仅是实现,还包括同步…

人工智能|机器学习——机器学习如何判断模型训练是否充分

一、查看训练日志 训练日志是机器学习中广泛使用的训练诊断工具,每个 epoch 或 iterator 结束后,在训练集和验证集上评估模型,并以折线图的形式显示模型性能和收敛状况。训练期间查看模型的训练日志可用于判断模型训练时的问题,例…

IOC DI入门

1.加上Component,控制翻转,将service和dao都交给IOC容器管理,成为IOC容器中的bean。用哪个类就在哪个类上面加component。 2.加上autowired。依赖注入。controller依赖于service,service依赖于dao。加上时,IOC容器会提…

Taro3+Vue3重构Mpvue小程序项目踩坑记

1、Taro小程序编译时报错; 原因:页面中存在小程序识别不了的标签;如div解决方法: 将div标签替换成小程序可识别的标签; 安装Taro中提供的插件:tarojs/plugin-html, 使其可被识别; 插件安装教程参考Taro官网&#xff1…

Matlab 点云曲率计算(之二)

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 之前已经讨论过许多关于计算曲率的问题,这里使用一个通过拟合三次曲面方程的方式来计算曲率,计算过程如下图所示: 二、实现代码 %********

springboot+bootstarp+jsp房屋租赁系统ssm_t65a9

小型房屋租赁系统主要有管理员、房东和租户三个功能模块。以下将对这三个功能的作用进行详情的剖析。 管理员模块:管理员是系统中的核心用户,管理员登录后,可以对后台系统进行管理。主要功能有个人中心、房东管理、租户管理、房源城市管理、房…

数据库应用:MongoDB 库与集合管理

目录 一、理论 1.MongoDB用户管理 2.MogoDB库管理 3.MogoDB集合管理 二、实验 1.MongoDB用户管理 2.MogoDB库管理 3.MogoDB集合管理 三、问题 1.不显示新创建的数据库 2.插入数据报错 3.删除指定数据库报错 一、理论 1.MongoDB用户管理 (1) 内置角色 数据库用户…

什么是高级语言、机器语言、汇编语言?什么是编译和解释?

1、高级语言 计算机程序是一种让计算机执行特定任务的方法。程序是由程序员用一种称为编程语言的特殊语言编写的。编程语言有很多种,例如 C、C、Java、Python 等。这些语言被称为高级语言,因为它们更接近人类的自然语言,而不是计算机能够直接…

【LeetCode刷题】--38.外观数列

38.外观数列 方法:遍历生成 该题本质上是依次统计字符串中连续相同字符的个数 例如字符串 1112234445666我们依次统计连续相同字符的个数为: 3 个连续的字符 1, 222 个连续的 2,1 个连续的字符 3,3个连续的字符 4,1个连续的字符…

创建一个带有背景图层和前景图层的渲染窗口

开发环境: Windows 11 家庭中文版Microsoft Visual Studio Community 2019VTK-9.3.0.rc0vtk-example demo解决问题: 创建一个带有背景图层和前景图层的渲染窗口,知识点:1. 画布转image;2. 渲染图层设置;3.…

如何高效批量生成条形码?

条形码作为商品、库存和信息管理的基础工具,扮演着至关重要的角色。为了满足用户对于高效、专业、多样化的条形码生成需求,我们推出了一款专业高效的在线条形码生成工具。 网址:https://www.1txm.com/ 多样化条形码支持 易条形支持多种常见…

Django请求生命周期流程

浏览器发起请求。 先经过网关接口,Django自带的是wsgiref,请求来的时候解析封装,响应走的时候打包处理,这个wsgiref模块本身能够支持的并发量很少,最多1000左右,上线之后会换成uwsgi,并且还会加…

Redis 主库挂了,如何不间断服务?

目录 1、哨兵机制的基本流程 2、主观下线和客观下线 3、如何选定新的主库? 总结 // 你只管前行,剩下的交给时间 在 reids 主从库集群模式下,如果从库发生故障了,客户端可以继续向主库或其他从库发送请求,进行相关的…

宠物网站的技术 SEO:完整指南

您是宠物行业网站的从业者吗?那么您一定知道,当人们寻找与宠物相关的资源时,在搜索引擎结果中排名靠前有多么重要。 这就是技术SEO的用武之地!它正在调整您网站的后端代码和服务器配置,以在 SERP 中排名更高。 在此&…

PCF8591多通道数据读取异常问题

问题描述 PCF8591在循环读取两个通道时,两个通道数据出现交错问题。 例如我们想实现:第一次读取通道一、第二次读取通道二、第三次读取通道一、第四次读取通道二……依次循环 但实际数据:第一次读取的值为0x80、第二次读取的值为通道一的值、…

西南科技大学C++程序设计实验二(类与对象一)

C++最大的特点就是面向对象,掌握它的几种基本性质还是好理解的,可以看我C++专栏的期末速成,希望对你们学习C++有帮助。 一、实验目的 1.理解简单类的定义、说明与使用 2.理解类中不同属性数据成员的访问特点 3.理解构造函数、析构函数的作用 重点:掌握类的定义与实现,…

java多线程-扩展知识一:进程线程、并发并行、同步异步

1、进程 进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程…

前端入门(三)Vue组件化编程、脚手架、插槽插件、存储、vuex、组件事件、动画、代理

文章目录 Vue 组件化编程 - .vue文件非单文件组件组件的注意点组件嵌套Vue实例对象和VueComponent实例对象Js对象原型与原型链Vue与VueComponent的重要内置关系 应用单文件组件构建 Vue脚手架 - vue.cli项目文件结构组件相关高级属性引用名 - ref数据接入 - props混入 - mixin …