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.代码:(贪心无套…

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…

Hive3.1.2——企业级调优

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

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

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

【运维测试】移动测试自动化知识总结第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 面向对象_私…

PS | 15个快捷键演示

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

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

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

位运算总结(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 …

文件压缩炸弹,想到有点后怕

今天了解到一个概念,压缩炸弹。 参考: https://juejin.cn/post/7289667869557178404 https://www.zhihu.com/zvideo/1329374649210302464 什么是压缩炸弹 压缩炸弹(也称为压缩文件炸弹、炸弹文件)是一种特殊的文件,它…

面试:大数据和深度学习之间的关系是什么?

大数据与深度学习之间存在着紧密的相互关系,它们在当今技术发展中相辅相成。 大数据的定义与特点:大数据指的是规模(数据量)、多样性(数据类型)和速度(数据生成及处理速度)都超出了传统数据处理软件和硬件能力范围的数据集。它具有四个主要特点,通常被称…

【Java】零基础蓝桥杯算法学习——二分查找

算法模板一: // 数组arr的区间[0,left-1]满足arr[i]<k,[left,n-1]满足arr[i]>k;Scanner scan new Scanner(System.in);int[] arr {1,2,3,4,5};int left 0,right arr.length-1;int k scan.nextInt();while(left<right) {//leftright时退出循环int mid (leftrigh…

leetcode(双指针)11.盛最多水的容器(C++详细解释)DAY9

文章目录 1.题目示例提示 2.解答思路3.实现代码结果 4.总结 1.题目 给定一个长度为 n 的整数数组 height 。有 n 条垂线&#xff0c;第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。 找出其中的两条线&#xff0c;使得它们与 x 轴共同构成的容器可以容纳最多的水。 返回…

【Django】Django项目部署

项目部署 1 基本概念 项目部署是指在软件开发完毕后&#xff0c;将开发机器上运行的软件实际安装到服务器上进行长期运行。 在安装机器上安装和配置同版本的环境[python&#xff0c;数据库等] django项目迁移 scp /home/euansu/Code/Python/website euansuxx.xx.xx.xx:/home…

OpenCV基础:用Python生成一幅随机的噪声图像

使用Python&#xff1a;生成一幅随机数值的灰度图像&#xff0c;图像大小为1616像素。借助OpenCV库。输出数值&#xff0c;并显示图像。 # -*- coding: utf-8 -*- """ Created on Wed Feb 14 21:49:09 2024author: 李立宗公众号&#xff1a;计算机视觉之光知识…

【QT+QGIS跨平台编译】之四十:【gsl+Qt跨平台编译】(一套代码、一套框架,跨平台编译)

文章目录 一、GSL介绍二、GSL下载三、文件分析四、pro文件五、编译实践一、GSL介绍 GSL(GNU Scientific Library)是一个开源的数值计算库,用于提供一系列常用的数学函数和算法。它为科学计算和数据分析提供了高效、可靠的工具。 GSL库提供了丰富的功能,包括数值积分、数值…

C++-带你深度理解string类的常见接口

1. 为什么学习string类&#xff1f; C语言中&#xff0c;字符串是以\0结尾的一些字符的集合&#xff0c;为了操作方便&#xff0c;C标准库中提供了一些str系列的库函数&#xff0c;但是这些库函数与字符串是分离开的&#xff0c;不太符合OOP的思想&#xff0c;而且底层空间需…

“从根到叶:深入理解堆数据结构“

​​​​​​​ 一.堆的概念及实现 1.1堆的概念 在数据结构中&#xff0c;堆是一种特殊的树形数据结构。堆可以分为最大堆和最小堆两种类型。 最大堆&#xff1a;对于堆中的任意节点&#xff0c;其父节点的值都不小于它的值。换句话说&#xff0c;最大堆中的根节点是堆中的最…