Redis核心技术与实战【学习笔记】 - 31.番外篇:Redis客户端如何与服务器端交换命令和数据

简述

Redis 使用 RESP 协议(Redis Serialzation Protocol)协议定义了客户端和服务器端交互的命令、数据的编码格式。在 Redis 2.0 版本中,RESP 协议正式称为客户端和服务器端的标准通信协议。从 Redis 2.0 到 Redis 5.0 ,RESP 协议都称为 RESP 2 协议,从 Redis 6.0 开始,Redis 就采用 RESP 3 协议了。


1.客户端和服务器端交互的内容有哪些?

RESP 2 协议是如何对命令和数据进行格式编码的呢?

我们可以把交互内容,分成客户端请求和服务器端响应两类:

  • 在客户端请求中,客户端会给 Redis 发送命令,以及要写入的键和值。
  • 而在服务器响应中,Redis 实例会返回:
    • 读取的值
    • OK 标识
    • 成功写入的元素个数
    • 错误信息
    • 以及命令(如 Redis Cluster 中的 MOVE 命令)

其实,这些交互内容还可以再进一步细分成 7 类:

  1. 命令:这就是针对不同数据类型的命令操作。例如对 String 类型的 SET、GET 操作,对 Hash 类型的 HSET、HGET 等,这些命令就是代表操作语义的字符串。
  2. :键值对中的键,可以直接用字符串表示。
  3. 单个值:对应 String 类型的数据,数据本身可以是字符串、数值(整数或浮点数),布尔值(True 或 False)等。
  4. 集合值:对应 List、Hash、Set、Sorted Set 类型的数据,不仅包含多个值,而且每个值也可以是字符串、数值或布尔值等。
  5. OK 回复:对应命令操作成功的结果,就是一个字符串的 OK
  6. 整数回复:这里有两种情况。
    • 一种是命令操作返回的结果是整数,例如 LLEN 命令返回列表的长度。
    • 另一种是集合命令成功操作时,实际操作元素的个数,例如 SADD 命令返回成功添加的元素个数。
  7. 错误信息:命令操作出错是的返回结果,包括 error 标识,以及具体的错误信息。

下面再接个三个具体的例子,帮助你进一步掌握这些交互内容。

第一个例子

# 成功写入String类型数据,返回 OK
127.0.0.1:6379> SET testkey testvalue
OK

这里的交互内容就包括了命令(SET 命令)、(String类型的键 testkey)和单个值(String 类型的 testvalue),而服务器端直接返回一个 OK 回复

第二个例子

# 成功写入Hash类型数据,返回实际写入的集合元素个数
127.0.0.1:6379> HSET testhash a 1 b 2 c 3
(integer) 3

这里的交互内容包括三个 key-value 的 Hash 集合值(a 1 b 2 c 3),而服务器端返回整数回复(3),表示成功写入的元素个数。

第三个例子

# 发送的命令不对,报错,并返回报错信息
127.0.0.1:6379> PUT testkey2 testvalue
(error) ERR unknown command 'PUT', with args beginning with: 'testkey2', 'testvalue'

这里的交互内容包括三个 key-value 的 Hash 集合值(a 1 b 2 c 3),而服务器端返回整数回复(3),表示成功写入的元素个数。

可以看到,这里的交互内容包括错误信息,这是因为,Redis 实例本身不支持 PUT 命令,所以服务器报错 error ,并返回具体的报错,也就是位置的命令 “PUT”。

2.RESP 2的编码格式规范

RESP 2 协议的设计目标是,希望 Redis 开发人员实现客户端时简单方便,这样可以减少客户端开发时出现的 Bug。而且,客户端和服务器端交互出现问题时,开发人员可以通过查看协议交互过程,能快速定位问题,方便调试。所以,RESP 2 协议采用了可读性很好的文本形式进行编码,也就是通过字符串,来表示各种命令和数据。

不过交互内容有多重,而且,实际传输的命令或数据也会有多个。针对这两种情况,RESP 2 协议在编码时设计了两个基本规范。

  1. 为了对不同类型的交互内容进行编码,RESP 2 协议实现了物资编码格式类型。同时,为了区分这 5 种编码类型,RESP 2 使用一个专门的字符,作为每种编码类型的开头字符。这样,客户端或服务器端进行解析时,可以直接通过开头字符串知道当前解析的编码类型。
  2. RESP 2 进行编码时,会按照单个命令或单个数据的粒度进行编码,并在每个编码结果后增加一个换行符 \r\n(有时也表示成 CRLF),表示一次编码结束。

接下来,分别结束下这 5 中编码类型。

2.1 简单字符串类型(RESP Simple String)

这种类型就是用一个字符串来进行编码,比如,请求操作在服务器端成功执行后的 OK 表示回复,就是这种类型进行编码的

当服务器端成功执行一个操作后,返回的 OK 标识就可以编码如下:

+OK\r\n

2.2 长字符串类型(RESP Bulk String)

这种类型是用一个二进制安全的字符串来进行编码。

这里的二进制安全,是相对于 C 语言中对字符串的处理方式来说的。 Redis 在解析字符串时,不会像 C 语言那样,使用 \0 来判定一个字符串的结尾,Redis 会把 \0 解析成正常的 0 字符,并使用额外的属性值表示字符串的长度。

对于 Redis\0Cluster\0 这个字符串来说,C 语言会解析为 “Redis”,而 Redis 会解析为 “Redis Cluster”,并用 len 属性字符串的真实长度是 14 字节,如下图所示:

在这里插入图片描述
这样一来,字符串中即使存储了 \0 字符,也不会导致 Redis 解析到 \0 时,就认为字符串结束了从而停止,这就保证了数据的安全性。和长字符串类相比,简单字符串就是非二进制安全的。

长字符串类型最大可以达到 512 MB,所以可以对很大的数据量进行编码,正好可以满足键值对本身的数据量需求,所以 RESP 2 就用这种类型对交互内容中的键或值进行编码,并且使用 $ 字符作为开头字符, $ 字符后面会紧跟一个数字,这个数字表示字符串的实际长度。

例如,我们使用 GET 命令读取一个键(假设为 testkey)的值(假设值为 testvalue)时,服务器端返回的 String 值编码如下,其中 $ 字符后的 9 ,表示数据长度为 9 个字符。

$9 testvalue\r\n

2.3 整数类型(RESP Integer)

这种数据类型也是一个字符串,但表示的是一个有符号 64 位整数。为了和包含数字的简答字符串类型区分开,整数类型使用 : 字符作为开头,可以用于对服务器端返回的整数回复进行编码。

例如,上面的第二个例子中,我们使用 HSET 命令设置了 testhash 的三个元素,服务器端实际返回的编码结果如下:

:3\r\n

2.4 错误类型(RESP Errors)

它是一个字符串,包括了错误类型和具体的错误信息。Redis 服务器端报错响应就是用这种类型进行编码的。RESP 2 使用 - 字符作为它的开头字符。

例如,上面的第三个例子中,我们在 redis-cli 执行 PUT testkey2 testvalue 命令报错,服务器端实际返回给客户端的报错编码结果如下:

-ERR unknown command 'PUT', with args beginning with: 'testkey2', 'testvalue'

其中 ERR 就是报错类型,表示一个统一错误,ERR 后面的文字内容就是具体的报错信息。

2.5 数组编码类型(RESP Arrays)

这是一个包含多个元素的数组,之前,元素的类型可以是刚刚介绍的 4 种编码类型。

在客户端发送请求和服务器端返回结果时,数组编码类型都能用的上。客户端在发送请求操作时,一般会同时包括命令和要操作的数据。而数组类型包含了多个元素,所以,就适合用来对发送的命令和数据进行编码。为了和其他类型区分开,RESP 2 使用 * 字符作为它的开头字符。

例如,我们执行 GET testkey,此时,客户端发送给服务器端的命令的编码结果就是使用数组类型编码的,如下所示:

*2\r\n$3\r\nGET\r\n$7\r\ntestkey\r\n
  • 其中,第一个 * 字符标识当前是数组类型的编码结果,2 标识该数组有 2 个元素,分别对应命令 GET 和 键 testkey。
  • 命令 GET 和 键 testkey,都是使用长字符串类型编码的,所以用 $ 字符加字符串长度来表示。

类型的,当服务器端返回包含多个元素的集合类型数据时,也会用 * 字符和元素个数作为标识,并用长字符串类型对返回的集合元素进行编码。

RESP 2 协议的 5 种编码类型和相应的开头字符,我在下表做了小结。

编码类型简单字符串长字符串整数错误数组
开头第一个字符+$:-*

3.RESP 2 的不足和 RESP 3 的改进

虽然 RESP 2 协议提供了 5 种编码类型,看起来很丰富,其实是不够的。比较,基本数据类型还包括很多,例如浮点数、布尔值等。编码类型偏少,会带来两个问题。

  • 一方面,在值的基本数据类型方面,RESP 2 只能区分字符串和证书,对于其他的数据类型,客户端使用 RESP 2 协议时,就需要进行额外的转换操作。例如,当一个浮点数用字符串表示时,客户端需要将字符串中的值和实际数字值比较,判断是否为数字值,然后再将字符串转换成实际的浮点数。
  • 另一方面,RESP 2 用数组类别编码表示所有的集合类型,但是,Redis 的集合类型包括了 List、Hash、Set 和 Sorted Set。当客户端接收到数组类型编码的结果时,还需要根据 调用的命令操作接口,来判断返回的数组究竟是哪一种集合类型。

假设一个 Hash 类型的键是 testhash,集合元素分别为 a:1、b:2、c:3。同时,有一个 Sorted Set 类型的键 testzset,集合的元素分别是 a、b、c,它们的分数分别是 1、2、3.我们在 redis-cli 客户端中读取它们返回的结果时,返回的形式都是一个数组:

127.0.0.1:6379> HGETALL testhash
1) "a"
2) "1"
3) "b"
4) "2"
5) "c"
6) "3"127.0.0.1:6379> ZRANGE testzset 0 3 withscores
1) "a"
2) "1"
3) "b"
4) "2"
5) "c"
6) "3"

为了在客户端按照 Hash 和 Sorted Set 两种类型处理代码中返回的数据,客户端还需要根据发送的命令操作 HGETALL 和 ZRANGE ,来把这两个编码的数组结果转换成相应的 Hash 集合和有序集合,增加了客户端额外的开销。

从 Redis 6.0 版本开始,RESP 3 协议增加了对多种数据类型的支持,包括:

  • 空值
  • 浮点数
  • 布尔值
  • 有序的字典集合
  • 无需的集合

RESP 3 也是通过不同的开头字符区分不同的数据类型,例如,当开头第一个字符是 ,,就表示接下来的结果是浮点数。这样一来,客户端就不用再通过额外的字符串对比,来实现数据转换操作了,提升了客户端的效率。

4.小结

RESP 2 协议题了 5 中类型的编码格式,包括简单字符串类型、长字符串类型、整数类型、错误类型和数组类型。为了区分这 5 种类型,RESP 2 协议使用了 5 种不同的开头字符作为这 5 种类型编码结果的第一个字符,分别是 +$:-*

RESP 2 协议是文本形式的协议,实现简单,可以减少客户端开发出现的 Bug,而且可读性强,便于开发调试。当你需要定制化 Redis 客户端时,就需要了解和掌握 RESP 2 协议。

RESP 2 协议的一个不足就是支持的类型偏少,所以 Redis 6.0 版本使用了 RESP 3 协议。和 RESP 2 协议相比,RESP 3 协议增加了对浮点数、布尔类型、有序字典集合、无序集合等多种类型数据的支持。不过,这里,有个地方需要你注意,Redis 6.0 只支持 RESP 3,对 RESP 2 协议不兼容,所以如果你使用 Redis 6.0 版本,需要确认客户端已经支持了 RESP 3 协议,否则将无法使用 Redis 6.0。

如果你想查看服务器端返回数据的 RESP 2 编码结果,就可以使用 telnet 命令和 Redis 实例连接,执行如下命令:

telnet 实例IP 实例端口

接着,给实例发送命令,这样就能看到 RESP 2 协议编码后的返回结果了。当然,你也可以在 telnet 中,想 Redis 实例发送 RESP 2 协议编写的命令,实例同样能处理。

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

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

相关文章

leetcode:55.跳跃游戏

1.解题思路:贪心算法看最大覆盖范围 2.模拟过程: 1.若数组长度等于1,直接返回True 2.循环遍历覆盖范围,选取最大的覆盖范围;若覆盖范围覆盖到了最后一个元素,直接返回true. 3.代码:(贪心无套…

Python struct.pack/struct.unpack 和 encoding/decoding的区别

一直觉得这两对函数有种微妙的对应,两者都涉及到数据的转换,struct.pack/struct.unpack 在二进制数据和 Python 对象之间进行转换,而 encoding/decoding 在文本数据和字节流之间进行转换 下面理一理它们的区别: 目的和用途&#…

ros自定义msg记录

文章目录 自定义msg1. 定义msg文件2. 修改 package.xml3. 修改 CMakeLists.txt4. message_publisher.py5. message_subscriber.py6. 运行 catkin build 测试 自定义msg ros 版本:kinetic 自定义test包的文件结构如下 |-- test | |-- CMakeLists.txt | |-- msg…

spring注解驱动系列--组件注入

一、spring组件注入的几种方式 1、Bean[导入的第三方包里面的组件] 2、包扫描组件标注注解(ComponentScans/ComponentScan Controller/Service/Repository/Component) 3、Import[快速给容器中导入一个组件] 4、使用Spring提供的 FactoryBean&#xff08…

Hive3.1.2——企业级调优

前言 本篇文章主要整理hive-3.1.2版本的企业调优经验,有误请指出~ 一、性能评估和优化 1.1 Explain查询计划 使用explain命令可以分析查询计划,查看计划中的资源消耗情况,定位潜在的性能问题,并进行相应的优化。 explain执行计划…

收藏:关于块存储,文件存储和对象存储

在B站上看到”【IT老齐465】“这个系列相当不错,每次的视频15分钟左右,出了400多个了,今天偶然看到,地址是:【IT老齐465】块存储、文件存储、对象存储的关系与区别_哔哩哔哩_bilibili 精彩摘录如下:

掘根宝典之C++有关返回对象的说明

返回指向const对象的引用 如果函数要返回(通过调用对象的方法或将对象作为参数)传递给他的对象,可以通过返回引用来提高其效率。 下面两种实现都是可以的 //这是可以的 AA MAX(const AA&t) { return t; } //这是不可以的 const AA&…

【运维测试】移动测试自动化知识总结第1篇:移动端测试介绍(md文档已分享)

本系列文章md笔记(已分享)主要讨论移动测试相关知识。主要知识点包括:移动测试分类及android环境搭建,adb常用命令,appium环境搭建及使用,pytest框架学习,PO模式,数据驱动&#xff0…

python入门篇11-面向对象的基础使用

全文目录,一步到位 1.前言简介1.1 专栏传送门1.1.1 上文小总结1.1.2 上文传送门 2. python基础使用2.1 面向对象的基础使用2.1.1 创建类2.1.2 使用对象(定义成员变量)2.1.3 成员方法的定义与使用2.1.4 构造方法的使用2.1.5 常用魔术方法 2.2 面向对象思想核心2.2.1 面向对象_私…

蓝桥杯(Web大学组)2022国赛真题:用什么来做计算 A

判分标准 实现重置(AC)功能,得 1 分。 实现计算式子和结果显示功能,得 3 分。 实现计算功能,得 6 分。 应该按要求来就行吧,,一开始还在想是否要考虑小数点个数的问题还有式子是否有效…… 笔记…

使用 GreenSock(GSAP)实现 字符串动画

要使用 GreenSock(GSAP)实现 "JianMa XinXi" 这个字符串的动画,其中两个 x 字符自动旋转,j 和 m 字符上下跳动,并且美化这个字符串使其可以作为 logo 使用,我们可以通过以下步骤来实现&#xff1…

Transformer实战-系列教程19:DETR 源码解读6(Transformer类)

🚩🚩🚩Transformer实战-系列教程总目录 有任何问题欢迎在下面留言 本篇文章的代码运行界面均在Pycharm中进行 本篇文章配套的代码资源已经上传 点我下载源码 DETR 算法解读 DETR 源码解读1(项目配置/CocoDetection类) …

PS | 15个快捷键演示

01 前言 工具:Adobe Photoshop 2021 安装:无 网上自查 02 快捷键表 Ctrl T自由变换[减小画笔大小]增加画笔大小Shift [降低画笔硬度Shift ]增加笔刷硬度D默认前景/背景颜色X切换前景/背景颜色Ctrl J通过复制新建图层Ctrl Shift J通过剪切新建图层Esc取…

树莓派4B(Raspberry Pi 4B)使用docker搭建单机版nacos [基于docker-compose]

树莓派4B(Raspberry Pi 4B)使用docker搭建单机版nacos [基于docker-compose] 镜像仓库提供的基于arm64架构的nacos镜像很少,我选用的是centralx/nacos-server ,它是基于nacos 2.0.4开发的。 ⚠️ 本文基于docker-compose记述构建单…

使用 WPF + Chrome 内核实现高稳定性的在线客服系统复合应用程序

对于在线客服与营销系统,客服端指的是后台提供服务的客服或营销人员,他们使用客服程序在后台观察网站的被访情况,开展营销活动或提供客户服务。在本篇文章中,我将详细介绍如何通过 WPF Chrome 内核的方式实现复合客服端应用程序。…

#Z2294. 打印树的直径

Description 给你一棵树,树上有N个点,编号从0到N-1 请找出任意一条树的直径,并输出直径上的点,输出顺序为从直径的某个端点走向另一个端点 Format Input 第一行一个整数 n; 之后 n-1 行每行两个整数 u,v&#xf…

位运算总结(Java)

目录 位运算概述 位运算符 位运算的优先级 位运算常见应用 1. 给定一个数n,判断其二进制表示中的第x位是0还是1 2. 将数n的二进制表示中的第x位修改为1 3. 将数n的二进制表示中的第x位修改为0 4. 位图 例题:判断字符是否唯一 5. 提取数n的二进制…

《区块链公链数据分析简易速速上手小册》第5章:高级数据分析技术(2024 最新版)

文章目录 5.1 跨链交易分析5.1.1 基础知识5.1.2 重点案例:分析以太坊到 BSC 的跨链交易理论步骤和工具准备Python 代码示例构思步骤1: 设置环境和获取合约信息步骤2: 分析以太坊上的锁定交易步骤3: 跟踪BSC上的铸币交易 结论 5.1.3 拓展案例 1:使用 Pyth…

OCP的operator——(2)OLM

文章目录 了解OperatorOperator Lifecycle Manager(OLM)OLM概念和资源OLM是什么OLM资源Cluster service version(CSV)Catalog source定制catalog source的image模板目录健康需求 SubscriptionInstall planOperator groupOperator …

SQL世界之命令语句Ⅴ

目录 一、SQL CREATE INDEX 语句 1.SQL CREATE INDEX 语句 2.SQL CREATE INDEX 语法 3.SQL CREATE UNIQUE INDEX 语法 4.SQL CREATE INDEX 实例 二、SQL 撤销索引、表以及数据库 1.SQL DROP INDEX 语句 2.SQL DROP TABLE 语句 3.SQL DROP DATABASE 语句 4.SQL TRUNCA…