介绍
参考官方文档
Redis的网络协议,名叫RESP(REdis Serialization Protocol ) ,设计的主要目标:
- 容易实现
- 快速解释
- 容易看懂
RESP可以序列化多种数据结构,包括数字、字符串、数组、错误信息等。 而且保证是二进制上的安全性,可以避免网络层面的传输错误,导致数据错误的问题。
Redis采取类似命令行的方式去处理请求的。 比如GET KEY1或SET KEY VALUE. 这种方式。 而传统数据库是采取SQL的方式去处理请求, 这时单纯处理SQL解释已经是比较复杂和耗时的。 尤其是复杂或大SQL。 但Redis更简单处理, 用类似命令行的方式隔开参数。 第一个参数对应的命令,后面的参数对应这个命令的参数。 对应网络协议层, 正是字符串类型的数组。 所以Redis解释完网络报文的同时,也等于解释完请求的参数。 这也是Redis执行非常高效的原因之一。
RESP版本
RESP现在主流有两个版本:2和3.
早在古老的版本,Redis 1.2 版本之前,有一个原始的版本RESP.
但在Redis 2.0版本之后,推出了主流的RESP 2版本,基本涵盖了主流的数据类型和表达能力。
到了Redis 6.0开始,推出了RESP 3协议。 RESP 3协议完全向下兼容RESP 2协议, 同时增加了更多的类型,提供了更好的表达能力。
直到现在2024年,主流默认的协议仍然是RESP 2协议,由于其极通用的使用场景。 但不排除后续一些新增Redis的功能只会出在RESP 3协议上。RESP协议介绍
RESP协议有一些通用的规则:
- 第一个字节作为类型标识
- 使用CRLF(即`\r\n`)作为分隔符, 协议的每个部分都是用这两个字节作为分隔符。
下面是协议的类型信息:
数据类型 | 最低的支持版本 | 分类 | 第一个字节 |
Simple strings | RESP2 | Simple | + |
Simple Errors | RESP2 | Simple | - |
Integers | RESP2 | Simple | : |
Bulk strings | RESP2 | Aggregate | $ |
Arrays | RESP2 | Aggregate | * |
Nulls | RESP3 | Simple | _ |
Booleans | RESP3 | Simple | # |
Doubles | RESP3 | Simple | , |
Big numbers | RESP3 | Simple | ( |
Bulk errors | RESP3 | Aggregate | ! |
Verbatim strings | RESP3 | Aggregate | = |
Maps | RESP3 | Aggregate | % |
Sets | RESP3 | Aggregate | ~ |
Pushes | RESP3 | Aggregate | > |
Simple strings
简单的字符类型。 以`+` 开始, CRLF(即`\r\n`)结尾,中间是字符内容,例如:
+OK\r\n
Simple errors
错误信息, 以`-` 开始, CRLF(即`\r\n`)结尾,中间是字符内容,例如:
-Error message\r\n
Integers
10进制的整数类型。对应是一个32-bit的interger类型, 格式如下:
:[<+|->]<value>\r\n
- 第一个字节是`:`
- 第二个节点可以是+或-号
- value必须都是数字
- 结尾是CRLF(即`\r\n`)
Bulk strings
字符类型,可以表达指定长度的字符串,解决了字符内容不能同时有CRLF的问题。一般最大只能支持512mb大小。
格式如下:
$<length>\r\n<data>\r\n
- 第一个字符是`$`
- length是长度,必须是整数
- 数字后使用CRLF(即`\r\n`)分割
- 内容,必须是length长度的字符串
- 结尾是CRLF(即`\r\n`)
其中length < 0 代表NULL字符串,比如
$-1\r\n
长度0的字符串如下:
$0\r\n\r\n
普通字符串如下:
$5\r\nhello\r\n
Arrays
数组类型,可以表达指定长度的数组类型。 数组的内容是嵌套其他任意类型的。 格式如下:
*<number-of-elements>\r\n<element-1>...<element-n>
- 第一个字符是`*`
- number-of-elements是长度,必须是整数
- 数字后使用CRLF(即`\r\n`)分割
- 内容,里面嵌套长度为number-of-elements的任意RESP数据类型
其中length < 0 代表NULL数组,比如
*-1\r\n
长度0的数组如下:
*0\r\n
普通数组类型,里面是字符串的如下:
*2\r\n$5\r\nhello\r\n$5\r\nworld\r\n
Nulls
NULL类型,是RESP 3新增的特殊类型, 数据格式如下:
_\r\n
BOOLEANS
布尔类型, 是RESP 3新增的数据类型,数据格式如下:
#<t|f>\r\n
- 第一个字节是`#`
- 第二个字节只能是t或f, 代表对应的true或false
- 结尾是CRLF(即`\r\n`)
Doubles
浮点类, 是RESP 3新增的数据类型,解决了RESP 2只能表达整数的问题。 数据格式如下:
,[<+|->]<integral>[.<fractional>][<E|e>[sign]<exponent>]\r\n
- 第一个字节是`,`
- 第二个字节可选是`+`或`-`
- integral比如是正整数
- integral后面可选接着`.`和正整数的fractional部分代表小数点后面的数字
- 可选E或e字节,sign是可选切只能`+`或`-`, exponent部分也只能是正整数
- 结尾是CRLF(即`\r\n`)
整体看起来是一个普通的浮点数或支持科学计数法的浮点数
比如下面的例子:
,1.23\r\n
也支持一些特殊的场景,比如正无穷,负无穷或没有这个数:
,inf\r\n
,-inf\r\n
,nan\r\n
Big numbers
10进制的整数类型,是RESP 3新增的数据类型。对应是一个64-bit的long类型, 格式如下:
([+|-]<number>\r\n
- 第一个字节是`(`
- 第二个节点可以是+或-号
- number必须都是数字
- 结尾是CRLF(即`\r\n`)
例如:
(3492890328409238509324850943850943825024385\r\n
Bulk errors
错误信息,是RESP 3新增的数据类型,参考Bulk String设计的错误信息类型。这样就可以表达换行的错误信息。格式如下:
!<length>\r\n<error>\r\n
参考例子:
!21\r\nSYNTAX invalid syntax\r\n
Verbatim strings
字符串类型,是RESP 3新增的数据类型,类似Bulk String, 增加了字符串的编码方式,参考格式如下:
=<length>\r\n<encoding>:<data>\r\n
- 第一个字节是`=`
- length是长度,必须是整数
- 数字后使用CRLF(即`\r\n`)分割
- encoding比如是3个字节
- `:` 隔开编码和数据
- data内容,必须是length-4长度的字符串
- 结尾是CRLF(即`\r\n`)
参考例子:
=15\r\ntxt:Some string\r\n
Maps
key-value的数据类型,是RESP 3新增的数据类型,类似ARRAY数据,但数量翻倍了。格式如下:
%<number-of-entries>\r\n<key-1><value-1>...<key-n><value-n>
- 第一个字节是`%`
- number-of-entries是长度,必须是整数
- 数字后使用CRLF(即`\r\n`)分割
- 内容,里面嵌套了2 * number-of-entries长度的RESP数据类型,其中单数是KEY,偶数是对应的VALUE
参考如果要表示下面的JSON
{
"first": 1,
"second": 2
}
对应的格式如下:
%2\r\n+first\r\n:1\r\n+second\r\n:2\r\n
Sets
类似Array数组格式,是RESP 3新增的数据类型, 只是保证代表的是SET数据类型,数据不会重复。 参考格式如下:
~<number-of-elements>\r\n<element-1>...<element-n>
- 第一个字节是`~`
- number-of-elements是长度,必须是整数
- 数字后使用CRLF(即`\r\n`)分割
- 内容,里面嵌套了number-of-elements个的RESP数据类型
Pushes
PUB/SUB订阅时推送的数据类型,是RESP 3新增的数据类型。RESP 3的协议层面上,支持单个连接复用消息推送。 这时需要使用到的特殊数据类型。 格式类似Array, 格式如下:
><number-of-elements>\r\n<element-1>...<element-n>
- 第一个字节是`>`
- number-of-elements是长度,必须是整数
- 数字后使用CRLF(即`\r\n`)分割
- 内容,里面嵌套了number-of-elements个的RESP数据类型
切换网络协议的方法
Redis 6 之后,新增了hello命令,用于参看当前的协议版本,以及支持切换协议版本,命令如下:
HELLO <protocol-version> [optional-arguments]
RESP的其他应用
RESP除了可以Redis的网络协议层,为了简化开发和功能实现,也在地方上面使用了。
Redis用于异常恢复的AOF文件,Redis重启后,需要从AOF文件文件里面读取增量的数据,以恢复完整的数据内容。 其中AOF的内容也是使用RESP格式报文,可以使用文本类工具查询。后续Redis恢复的时候,逻辑也是模拟一个前端连接,重复执行一系列AOF文件里面的命令。 这样也简化了恢复的实现。但这种算是逻辑备份和恢复, 一般来说性能和存储使用率上会比数据库常见的物理备份更差。