Redis入门指南学习笔记(3):Redis高级特性

一.前言

上一篇博客对Redis常用的数据结构进行了详细介绍。Redis除了丰富的数据类型支持,还包含许多高级特性,例如事务、内存驻留策略、排序、消息队列等,本文将对这些进行逐一介绍。

二.事务

Redis同样包含事务(transaction)的概念,事务是一组命令的集合,事务中的命令要不全部都执行,要不都不执行。

下面展示的便是Redis事务用法。

> MULTI # 事务的开始, 告知Redis下面的命令属于同一个事务, 先不要执行, 而是暂存起来。
"OK"> DEL cpp
"QUEUED" # 表示命令已经放入等待执行的事务队列中> DEL program
"QUEUED"> EXEC # 告知Redis将事务队列中的命令按顺序执行
1) "1"
2) "1"

Redis事务还可以保证一个事务内的命令依次执行而不被其他命令插入。

2.1 错误处理

事务中的命令可能会出错,错误原因主要包含语法错误运行错误两种,针对两种错误类型,Redis采用了不同的应对策略。

语法错误

语法错误主要指命令不存在或者命令参数不对。下面便展示了一个语法错误的例子,事务中总共添加了两条命令,其中第二条语法出错,执行EXEC后可以看到Redis会直接返回错误,事务中语法正确的命令也不会执行

> MULTI
"OK"> SET money 11
"QUEUED"> GET money 11
"ERR wrong number of arguments for 'get' command"> EXEC
"EXECABORT Transaction discarded because of previous errors."

运行错误

运行错误指在符合语法但执行过程中出现的错误,该类错误是由程序员造成的。对于该种类型,事务里的错误命令不会导致其他命令无法执行

下面展示的是一个运行错误的例子,事务的第二天命令错误的对非整数形式的字符串类型应用了自增命令INCR,在执行EXEC后可以看到,第一条命令成功执行,第二条命令返回错误信息。

> MULTI
"OK"> GET JAPAN
"QUEUED"> INCR JAPAN
"QUEUED"> EXEC
1) "Tokyo"
2) "ReplyError: ERR value is not an integer or out of range"

Redis没有提供回滚(rollback)功能,开发者需要为自己写的命令负责。

2.2 WATCH命令

WATCH命令可以用来监控一个或多个键,一旦其中有一个键被修改或删除,之后的事务就不会执行,监控一直持续到EXEC命令。

执行EXEC命令后会取消对所有键的监控,若不想执行事务中的命令,也可以使用UNWATCH命令来取消监控

下面例子中,WATCH命令监控键count之后对其进行了自增操作,之后又进入了一个事务,从结果可以看出,事务并没有执行。

> SET count 1
"OK"> WATCH count
"OK"> INCR count
(integer) 2> MULTI
"OK"> INCR count
"QUEUED"> EXEC
(nil)> GET count
"2"

三.过期时间

Redis可以使用EXPIRE命令设置一个键的过期时间,到期后Redis会自动删除它,该命令具体的形式为:

# seconds表示键过期的时间,单位是秒
EXPIRE key seconds

使用示例如下,这里设置了一个折扣discount的过期时间为10秒,其中TTL命令是用来查看一个键还有多久被删除的,-2表示键已经不存在,对永久存在的键应用TTL命令返回的是-1

> SET discount 8.5
"OK"> EXPIRE discount 10
(integer) 1> TTL discount
(integer) -2> EXISTS discount
(integer) 0

若想清除键的过期时间设置,可以使用PERSIST命令,若清除成功返回1,否则返回0。

> SET discount 8
"OK"> EXPIRE discount 20
(integer) 1> TTL discount
(integer) 17> PERSIST discount
(integer) 1> TTL discount
(integer) -1

使用SETGETSET命令为键赋值同样也会清除键的过期时间。

> GET discount
"8"> EXPIRE discount 20
(integer) 1> TTL discount
(integer) 17> SET discount 7
"OK"> TTL discount
(integer) -1

四.排序

在Redis中倘若需要数据有序有多种方法,下面将对其进行逐一介绍。

4.1 有序集合

Redis提供了有序集合类型ZSET,其用法形式如下:

# score和member一一对应, score是排序的依据
ZADD key score member [score member ...]  

使用示例为:

> ZADD rank 90 xm 88 xh 95 xz 86 xl
(integer) 4> ZRANGE rank 0 -1
1) "xl"
2) "xh"
3) "xm"
4) "xz"

可以看到默认为升序排序。

4.2 SORT命令

SORT命令可以对列表类型、集合类型和有序集合类型键进行排序。

SORT命令的时间复杂度为 O ( n + m log ( m ) ) O(n + m \text{log}(m)) O(n+mlog(m)),其中n表示待排序的列表/集合中的元素个数,m表示要返回元素的个数。

SORT的使用示例如下:

> LPUSH scores 87 77 90 68 100
(integer) 5> SORT scores
1) "68"
2) "77"
3) "87"
4) "90"
5) "100"> SORT scores DESC
1) "100"
2) "90"
3) "87"
4) "77"
5) "68"

SORT命令还可以通过ALPHA参数实现按字典序排列非数字元素。

> LPUSH namelist tom james shelly sala andy
(integer) 5> LRANGE namelist 0 -1
1) "andy"
2) "sala"
3) "shelly"
4) "james"
5) "tom"> SORT namelist ALPHA
1) "andy"
2) "james"
3) "sala"
4) "shelly"
5) "tom"

SORT与SQL一样也支持使用LIMIT offset count来指定跳过前offset个元素并获取之后的count个元素。

> SORT scores DESC LIMIT 0 3
1) "100"
2) "90"
3) "87"

使用STORE描述符可以将排序的结果存储的指定的键上

> SORT scores DESC STORE sorted_scores
(integer) 5> LRANGE sorted_scores 0 -1
1) "100"
2) "90"
3) "87"
4) "77"
5) "68"

SORT还可以通过BY参数来指定排序的模式,其语法形式为BY 参考键,参考键可以是字符串类型键或哈希类型键的某个字段(键名->字段名)。

> LPUSH students student:1 student:2 student:3 student:4
(integer) 4> HMSET student:1 age 18 name Tom
"OK"> HMSET student:2 age 17 name Shelly
"OK"> HMSET student:3 age 20 name Andy
"OK"> HMSET student:4 age 18 name Jack
"OK"# 按年龄排序
> SORT students BY *->age
1) "student:2"
2) "student:1"
3) "student:4"
4) "student:3"# 按名字排序
> SORT students BY *->name ALPHA
1) "student:3"
2) "student:4"
3) "student:2"
4) "student:1"

SROT还包含GET参数,该参数不影响排序,它的作用是使SORT命令的返回结果不再是元素自身的值,而是GET参数中指定的键值。

# 根据年龄排序,并返回学生名字
> SORT students BY *->age GET *->name
1) "Shelly"
2) "Tom"
3) "Jack"
4) "Andy"

五.消息通知

通知可以借助任务队列实现,任务队列就是传递任务的队列,与任务队列交互的实体有生产者(将需要处理的任务放入任务队列)和消费者(不从任务队列中读取任务消息并执行)。

使用任务队列有如下好处:

  • 松耦合:生产者和消费者无须知道彼此的实现细节,只需要约定好任务的描述格式。
  • 易于扩展:可以有多个消费者,且可以分布在不同的服务器上,从而减轻单台服务器的负载。

5.1 基于列表的队列

可以列表可以实现队列,即让生产者在一边使用LPUSH命令将任务加入到列表中,然后让消费者在另一边使用RPOP不断从里列表中取出任务执行。但采用RPOP有一点不完美之处,当任务队列中没有任务时,需要不断调用RPOP去检查,而使用BRPOP命令则不会有这个问题,当列表中没有元素时,BRPOP命令会不断阻塞,直到有新元素加入

BRPOP命令接受两个参数,第一个是键名,第二个是超时时间,单位为秒,当到达设定的超时时间还没有新元素时,则返回nil。该命令会返回两个值,分别是键名和元素值

> LPUSH message m1 m2 m3
(integer) 3> BRPOP message 1
1) "message"
2) "m1"

BRPOP可以同时接收多个键,其语法形式为BRPOP key [key ...] timeout,若所有键都没有元素则阻塞,否则会按顺序弹出第一个非空列表中的元素。

> LPUSH message1 m1 m2 m3
(integer) 3> LPUSH message2 m4
(integer) 1> BRPOP message1 message2 1
1) "message1"
2) "m1"> BRPOP message1 message2 1
1) "message1"
2) "m2"> BRPOP message1 message2 1
1) "message1"
2) "m3"> BRPOP message1 message2 1
1) "message2"
2) "m4"> BRPOP message1 message2 1
(nil)

5.2 发布订阅模式

Redis还提供了一组命令可以实现发布订阅模式,该模式中包含两种角色,发布者和订阅者,订阅者可以订阅一个或多个频道(channel),发布者可以想指定频道发送消息,只有订阅该频道的订阅者才能收到相关的消息。

发布者发布消息的命令为PUBLISH channel message,其返回值为接收到这条消息的订阅者的数量。订阅频道的命令为SUBSCRIBE channel [channel ...],从语法可以看出可以订阅多个频道。

# 客户端1
# 订阅频道, 等待和接收消息
> SUBSCRIBE mchannel
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "mchannel"
3) (integer) 1
1) "message"
2) "mchannel"
3) "hello world"# 客户端2
# 往频道发布消息
> PUBLISH mchannel "hello world"
(integer) 1

从上面可以看到,客户端可以收到3种类型的回复,每种类型的回复都包含3个值,第一个是消息的类型,第二、三个视消息类型而不同。

消息类型包括:

  • subscribe:表示订阅成功的反馈信息。第二个值为订阅的频道名,第三个值为当前客户端订阅的频道数量。
  • message:表示接收到的消息,第二个值为产生消息的频道名,第三个值为消息的内容。
  • unscribe:表示成功取消订阅某个频道,第二个为要取消的频道名,第三个是当前客户端订阅的频道数量。

通过UNSUBSCRIBE channel [channel ...]可以取消订阅的频道。

5.3 强大的流

Redis 5.0引入了全新的流类型,流类型除了能高效存储日志结构的数据,还可以作为消息中间件。作为消息中间件时,其与上述介绍的列表、发布/订阅模式的对比如下表所示:

列表-发布订阅模式对比

由上图可见,流同时具备列表和发布/订阅模式的所有优点

流的基本用法

流类型具备日志的特征,一是仅在末尾追加,二是每一条记录都包含了多个结构化的信息

流在插入新条目时会为其自动生成一个在流中唯一的ID,这个ID可以用来进行查询操作。

往流中增加条目的命令为:

XADD key [MAXLEN [=|~] threshold] *|ID field value [field value ...]

其中:

  • [MAXLEN [=|~] threshold]用来指定流最多保持指定数量的条目,~表示近似裁剪,即非精确地保持thresold条,而是可以稍微多一点,这样可以提升性能。
  • *|ID用来指定条目的ID,其中*的含义是让Redis按规则自动生成,也可以由用户主动指定ID。
  • field value [field value ...]表示若干字段及其对应的字段值。

添加条目的示例如下:

> XADD logs MAXLEN ~ 20 * IP 172.146.5.3 status 200
"1699929848274-0"> XADD logs MAXLEN ~ 20 * IP 172.146.5.6 status 304
"1699930100371-0"

由上示例可知ID的格式为<millisecondsTime>-<sequenceNumber>,其中前半部分为Redis服务器的时间戳,后半部分为序列号。

流的查询可以使用如下命令,其中startend表示条目的ID,查询时ID中的序列号部分可以省略,通过这种方式,可以查询指定范围内的条目列表。COUNT可以用来限制返回结果的条数。

XRANGE key start end [COUNT count]

此外,xRANGE还支持两个特殊的ID,即-+,分别表示最小ID和最大ID,通过这两个特殊ID可以获取流中所有的条目

> XRANGE logs - +
1) 1) "1699929848274-0"2) 1) "IP"2) "172.146.5.3"3) "status"4) "200"
2) 1) "1699930100371-0"2) 1) "IP"2) "172.146.5.6"3) "status"4) "304"

流对消息中间件的支持

流提供了高效查询消息历史的命令,并可以自定义从哪个ID开始读。具体通过XREAD命令实现。

XREAD命令可以从流中读取数据,其语法形式为:

XREAD [COUNT count] [BLOCK milliseconds] STREAMS key [key ...] ID [ID ...]

其中count表示读取的条目数,BLOCK表示阻塞多久,单位是毫秒,STREAMS参数后是要读取键名,以及从哪个ID开始读。

命令示例为:

> XREAD COUNT 1 STREAMS logs 1699929848274-0
1) 1) "logs"2) 1) 1) "1699930100371-0"2) 1) "IP"2) "172.146.5.6"3) "status"4) "304"

由上可知,XREAD返回数据是一个列表,最外层列表中每个元素对应XREAD命令请求的键名,接下来每个结果中包含条目ID和条目中所有的键值对。

5.4 流与消费组

消费组中包含若干消费者,消费组在对外接收消息时可以视为一个整体,当消费组接收一条消息时,会将该消息分发给组内的其中一个消费者,即同一条消息不会发给组内的多个消费者。例如下图中包含5个消费者,其中有2个消费者处于同一个消费组中,故它们各自只收到了一条消息,而其他的消费者能收到所有的消息。

消费者与消费者组

创建消费者组的命令为:

XGROUP [CREATE key groupname] ID|$ [MKSTREAM]

其中MKSTREAM参数用来说明若指定的流不存在,则会创建一个流,若不指明该参数的话,流不存在会返回一个错误。符号$用来说明当前消费组的ID,其表示最后一个条目,也可以指定具体的ID。

消费组的创建示例为:

> XGROUP CREATE mystream mygroup $ MKSTREAM
OK

消费者组消费消息的命令为XREADGROUP,该命令与XREAD的定义类似,具体语法为:

XREADGROUP GROUP group consumer [COUNT count] [BLOCK milliseconds] [NOACK] STREAMS key [key ...] id [id ...]

往流mystream中添加消息,两个属于同一个消费组mygroup的消费者消费流中消息的示例为:

# 往消费组添加消息
> XADD mystream * message A
"1700632675587-0"
> XADD mystream * message B
"1700632678571-0"# 客户端
> XREADGROUP GROUP mygroup consumer1 COUNT 1 STREAMS mystream >
1) 1) "mystream"2) 1) 1) "1700632675587-0"2) 1) "message"2) "A"# 客户端2
> XREADGROUP GROUP mygroup consumer2 COUNT 1 STREAMS mystream >
1) 1) "mystream"2) 1) 1) "1700632678571-0"2) 1) "message"2) "B"

上述命令中末尾的>表示还没有分发的消息ID,消费组读取要提供ID是为了进行消息确认。消费者组内消费者读取每个条目并正确处理后,都必须通过XACK命令告诉消费者组处理成功,否则该条目会被加入该消费者的等待队列,直接到接收到XACK命令,或用XCLAIM命令将这条消息转移给组内的其他消费者重新处理。

XACK命令语法形式为:

XACK key group ID [ID ...]

其中key表示流,group表示消费者组,ID表示消费消息的ID。

消费者2确认处理成功的示例如下:

> XACK mystream mygroup 1700632678571-0
(integer) 1

消费者组内维护了每个消费者的等待队列,可以通过XPENDING命令查看:

XPENDING key group

使用示例为:

> XACK mystream mygroup 1700632678571-0
(integer) 1
> XPENDING mystream mygroup
1) (integer) 1
2) "1700632675587-0"
3) "1700632675587-0"
4) 1) 1) "consumer1"2) "1"

上述返回结果中第一行表示,mygroup消费者组内所有消费者的等待队列中包含的条目数,接下来两行表示条目的最大和最小ID,紧接着是有等待条目的消费者名称和相应队列的条目数量。

六.结语

参考资料:

  • 《Redis入门指南》 李子骅编著

以上便是文本的全部内容,若有任何错误敬请批评指正,要是觉得不错可以点赞或关注一下,后续会持续更新。

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

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

相关文章

Vue2系列 — 渲染函数 (render + createElement)

官网文档&#xff1a;https://v2.cn.vuejs.org/v2/guide/render-function.html 1 render 函数 render 函数 不使用模板&#xff0c;使用 js 生成虚拟 dom 2 createElement() 接受的参数&#xff1a; 参数1 节点类型参数2 attribute参数3 子节点 3 DEMO <template>&…

AppLink结合金蝶云星空作订单信息同步流程

此次通过AppLink&#xff0c;根据请求数据金蝶云星空做销售订单信息同步拉取 在获取订单信息前需要得到金蝶云星空授权&#xff0c;详细授权步骤可查看&#xff1a;金蝶云星空授权指南 根据请求数据在金蝶云星空保存销售订单 当webhook接收到数据时触发流程 步骤1&#xff…

【数据库】数据库中的备份与恢复,保障容灾时的数据一致性与完整性

数据库的备份机制 ​专栏内容&#xff1a; 手写数据库toadb 本专栏主要介绍如何从零开发&#xff0c;开发的步骤&#xff0c;以及开发过程中的涉及的原理&#xff0c;遇到的问题等&#xff0c;让大家能跟上并且可以一起开发&#xff0c;让每个需要的人成为参与者。 本专栏会定期…

oracle数据库常见巡检脚本-系列一

简介 作为数据库管理员&#xff08;DBA&#xff09;&#xff0c;定期进行数据库的日常巡检是非常重要的。以下是一些原因&#xff1a; 保证系统的稳定性&#xff1a;通过定期巡检&#xff0c;DBA可以发现并及时解决可能导致系统不稳定的问题&#xff0c;如性能瓶颈、资源利用率…

详解自动化之单元测试工具Junit

目录 1.注解 1.1 Test 1.2 BeforeEach 1.3 BeforeAll 1.4 AfterEach 1.5 AfterAll 2. 用例的执行顺序 通过 order() 注解来排序 3. 参数化 3.1 单参数 3.2 多参数 3.3 多参数(从第三方csv文件读取数据源) 3.4 动态参数ParameterizedTest MethodSource() 4. 测试…

机器学习第12天:聚类

文章目录 机器学习专栏 无监督学习介绍 聚类 K-Means 使用方法 实例演示 代码解析 绘制决策边界 本章总结 机器学习专栏 机器学习_Nowl的博客-CSDN博客 无监督学习介绍 某位著名计算机科学家有句话&#xff1a;“如果智能是蛋糕&#xff0c;无监督学习将是蛋糕本体&a…

vue3-组合式API

​&#x1f308;个人主页&#xff1a;前端青山 &#x1f525;系列专栏&#xff1a;Vue篇 &#x1f516;人终将被年少不可得之物困其一生 依旧青山,本期给大家带来vue篇专栏内容:vue3-组合式API 目录 组合式API 1.1 什么是组合式API 1.2 为什么使用它 1.2.1 更好的逻辑复用#…

“云浮云福保”暖心回归! 保障升级价格不变,医保个账可为全家缴费!

11月22日&#xff0c;2024年“云浮云福保”项目启动会在广东省云浮市迎宾馆成功举办。记者在会上获悉&#xff0c;“云浮云福保”是在云浮市医疗保障局、云浮市金融工作局、国家金融监督管理总局云浮监管分局指导下&#xff0c;的指导下&#xff0c;由中国人民财产保险股份有限…

高斯Filter 和 Bilateral Filter

参考链接&#xff1a; Python | Bilateral Filtering - GeeksforGeeks 高斯Filter&#xff1a; 高斯模糊后的图像中的每个像素的强度是由它周围的像素的加权平均得到的&#xff0c;这个权重就是高斯函数的值&#xff0c;它取决于像素之间的距离。具体来说&#xff1a; 通常会导…

PostMan接口测试教程

1、下载和安装 Postman: 前往 Postman 官网 &#xff08;https://www.postman.com&#xff09;&#xff0c;下载适用于你的操作系统的 Postman 客户端。 执行下载后的安装程序&#xff0c;并按照安装向导的指引完成安装过程。 2、创建一个新的集合&#xff1a; 打开 Postma…

引爆关注,聚焦上海新闻媒体邀请

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 上海拥有众多的新闻媒体机构&#xff0c;包括报纸、电视、广播和网络媒体等。这些媒体在报道国内外新闻、传播信息等方面发挥着重要作用。 其中&#xff0c;上海电视台是上海最大的电视…

邦永PM2项目管理系统 SQL注入漏洞复现

0x01 产品简介 邦永PM2项目管理系统科学地将项目管理思想和方法和谐、统一&#xff0c;使得长期以来困扰项目管理工作者的工期、进度、投资和成本情况无法整体动态管理的问题得到了全面而彻底的解决。 0x02 漏洞概述 邦永科技PM2项目管理平台Global_UserLogin.aspx接口处未对用…

解决ESP32内部RAM内存不足的问题

一&#xff0c;为什么需要外部RAM ESP32有520kB的内部RAM空间可以使用&#xff0c;这对于一般的情况是够用的&#xff0c;但是如果设备需要涉及音频或者显示图像等处理时&#xff0c;需要更大的内存空间来处理这些数据。ESP32支持扩展外部RAM&#xff0c;其实乐鑫已经在其ESP32…

接口自动化中cookies的处理技术

一&#xff0c;理论知识 为什么有cookie和session&#xff1f; 因为http协议是一种无状态的协议&#xff0c;即每次服务端接受到客户端的请求时都时一个全新的请求&#xff0c;服务器并不知道客户端的请求记录&#xff0c;session和cookie主要目的就是弥补http的无状态特性 …

G1垃圾收集器

G1收集器(-XX:UseG1GC) 前置文章&#xff1a;JVM垃圾收集器 G1 (Garbage-First)是一款面向服务器的垃圾收集器&#xff0c;主要针对配备多颗处理器及大容量内存的机器。以极高概率满足GC停顿时间要求的同时&#xff0c;还具备高吞吐量性能特征。 G1将Java堆划分为多个大小相等…

【LeetCode刷题】-- 29.两数相除

29.两数相除 思路&#xff1a; class Solution {public int divide(int dividend, int divisor) {//考察被除数为最小值的情况if(dividend Integer.MIN_VALUE){//被除数为最小值&#xff0c;除数是1&#xff0c;返回最小值if(divisor 1){return Integer.MIN_VALUE;}//除数是-…

口碑好的猫罐头有哪些?宠物店受欢迎的5款猫罐头推荐!

快到双十二啦&#xff01;铲屎官们是时候给家里猫主子囤猫罐头了。许多铲屎官看大促的各种品牌宣传&#xff0c;看到眼花缭乱&#xff0c;不知道选哪些猫罐头好&#xff0c;胡乱选又怕踩坑。 口碑好的猫罐头有哪些&#xff1f;作为一个经营宠物店7年的老板&#xff0c;活动期间…

二分查找——经典题目合集

文章目录 &#x1f99c;69. x 的平方根&#x1f33c;题目&#x1f33b;算法原理&#x1f337;代码实现 &#x1f433;35. 搜索插入位置&#x1f33c;题目&#x1f33b;算法原理&#x1f337;代码实现 &#x1f9ad;852. 山脉数组的峰顶索引&#x1f33c;题目&#x1f33b;算法原…

python-opencv划痕检测-续

python-opencv划痕检测-续 这次划痕检测&#xff0c;是上一次划痕检测的续集。 处理的图像如下&#xff1a; 这次划痕检测&#xff0c;我们经过如下几步: 第一步&#xff1a;读取灰度图像 第二步&#xff1a;进行均值滤波 第三步&#xff1a;进行图像差分 第四步&#xff1…

java创建指定分辨率的图片或修改图片的分辨率(DPI)

因为java默认的图片像素分辨率DPI72&#xff0c;分辨率有点低。所以研究了一下如何创建指定DPI的方案。 DPI&#xff1a; 指的是每英尺的像素点(dots per inch) JPEG图片 JPEG图片的元数据定义参看oracle官网。 https://docs.oracle.com/javase/8/docs/api/javax/imageio/me…