page contents

Redis 之 RESP 协议

难点是保证redis-cli在pipe mode模式下执行和netcat一样快的同时,如何能理解服务器发送的最后一个回复。

attachments-2020-08-icAl6IsM5f476c36d614e.jpg

RESP 协议简介


Redis 的客户端和服务端之间在 TCP 协议的上层采用一种独立名为 RESP(REdis Serialization Protocol) 协议作为进行通讯的标准方式。

Redis 协议在以下几点之间做出了折衷:

  • 简单的实现
  • 快速地被计算机解析
  • 简单得可以能被人工解析

新的统一协议已在Redis 1.2中引入,但是在Redis 2.0中,这就成为了与Redis服务器通讯的标准方式。在这个统一协议里,发送给Redis服务端的所有参数都是二进制安全的。Redis用不同的回复类型回复命令。它可以从服务器发送的第一个字节开始校验回复类型:

  • 单行回复(单行字符串回复),回复的第一个字节将是“+”
  • 错误消息(单行字符串回复的另外展示形式),回复的第一个字节将是“-”
  • 整型回复(正整形数字回复),回复的第一个字节将是“:”
  • 批量回复(多行字符串回复),回复的第一个字节将是“$”
  • 多个批量回复(数组回复),回复的第一个字节将是“*”


传输层及网络层


传输层仍然是底层 TCP 传输。Redis在TCP端口6379上监听到来的连接,客户端连接到来时,Redis服务器为此创建一个TCP连接。在客户端与服务器端之间传输的每个Redis命令或者数据都以\r\n结尾。Redis接收由不同参数组成的命令。一旦收到命令,将会立刻被处理,并回复给客户端。


关于校验回复类型


单行回复(单行字符串回复):"+"

状态回复(或者单行回复)以“+”开始以“\r\n”结尾的单行字符串形式。例如:

> set name leeprince
+OK\r\n  # 服务端实际返回
---
OK # redis-cli 客户端显示


错误消息(单行字符串回复的另外展示形式):"-"

错误回复发送类似于状态回复。唯一的不同是第一个字节用“-”代替“+”。
错误回复仅仅在一些意料之外的事情发生时发送,例如:如果你试图执行一个操作来应付错误的数据类型,或者如果命令不存在等等。所以当收到一个错误回复时,客户端将会出现一个异常。例如:

> leeprince
-(error) ERR unknown command `leeprince`, with args beginning with:\r\n  # 服务端实际返回
---
(error) ERR unknown command `leeprince`, with args beginning with:  # redis-cli 客户端显示


整型回复(正整形数字回复):":"

这种回复类型只是用CRLF结尾字符串来表示整型,用一个字节的“:”作为前缀。例如:“:0\r\n”,或者“:1000\r\n”是整型回复。
像INCR或者LASTAVE命令用整型回复作为实际回复值,此时对于返回的整型没有特殊的意思。它仅仅是为INCR、LASTSAVE的UNIX时间等增加数值。
一些命令像EXISTS将为true返回1,为false返回0。
其它命令像SADD、SREM和SETNX如果操作实际完成了的话将返回1,否则返回0。例如:

>rpush intkey intvalue01 intvalue02 intvalue03
:3\r\n   # 服务端实际返回
---
(integer) 3 # redis-cli 客户端显示

以下命令都是回复一个整型:

SETNX、DEL、EXISTS、INCR、INCRBY、DECR、DECRBY、DBSIZE、LASTSAVE、RENAMENX、MOVE、LLEN、SADD、SREM、SISMEMBER、SCARD。


批量回复(多行字符串回复):”$“

批量回复被服务器用于返回一个单二进制安全字符串。服务器发送第一行回复,该行以“$”开始后面跟随实际要发送的字节数,随后是CRLF,然后发送实际数据,随后是2个字节的额外数据用于最后的CRLF。例如:

>get bulkkey
$9\r\nbulkvalue\r\n   # 服务端实际返回
---
"bulkvalue"  # redis-cli 客户端显示

如果请求的值不存在,批量回复将使用特殊的值-1来作为数据长度,例如:

>get bulkkey_
$-1 # 服务端实际返回
---
(nil) # redis-cli 客户端显示


多个批量回复(数组回复):”*“

像命令LRNGE需要返回多个值(列表的每个元素是一个值,而LRANGE需要返回多于一个单元素)。使用多批量写是有技巧的,用一个初始行作为前缀来指示多少个批量写紧随其后。批量回复的第一个字节总是*,例如:

127.0.0.1:6379> rpush arraykey a01 a0101
(integer) 2 # redis-cli 客户端显示
127.0.0.1:6379> lrange arraykey 0 -1
# 服务端实际返回
*2\r\n$3\r\na01$5\r\na0101\r\n  
--- 
# redis-cli 客户端显示    
1) "a01"
2) "a0101"

正如您可以看到的多批量回复是以完全相同的格式使用Redis统一协议将命令发送给服务器。
服务器发送的第一行是*4\r\n,用于指定紧随着4个批量回复。然后传送每个批量写。
如果指定的键不存在,则该键被认为是持有一个空的列表,且数值0被当作多批量计数值来发送,例如:

127.0.0.1:6379> LRANGE nokey 0 1
# 服务端实际返回
*0
---
# redis-cli 客户端显示    
(empty list or set)

当BLPOP命令超时时,它返回nil多批量回复。这种类型多批量回复的计数器是-1,且值被当作nil来解释。例如:

127.0.0.1:6379> BLPOP nokey 1
# 服务端实际返回
*-1
---
# redis-cli 客户端显示 
(nil)
(1.09s)

当这种情况发生时,客户端库API将返回空nil对象,且不是一个空列表。这必须有别于空列表和错误条件(例如:BLPOP命令的超时条件)。


多命令和管道(pipelining)


客户端能使用同样条件为了发出多个命令。管道用于支持多命令能够被客户端用单写操作来发送,它不需要为了发送下一条命令而读取服务器的回复。所有回复都能在最后被读出。
通常Redis服务器和客户端拥有非常快速的连接,所以在客户端的实现中支持这个特性不是那么重要,如果一个应用需要在短时间内发出大量的命令,管道仍然会非常快。


管道(Pipelining) VS 脚本(Scripting)


大量 pipeline 应用场景可通过 Redis 脚本(Redis 版本 >= 2.6)得到更高效的处理,后者在服务器端执行大量工作。脚本的一大优势是可通过最小的延迟读写数据,让读、计算、写等操作变得非常快(pipeline 在这种情况下不能使用,因为客户端在写命令前需要读命令返回的结果)。
应用程序有时可能在 pipeline 中发送 EVAL 或 EVALSHA 命令。Redis 通过 SCRIPT LOAD 命令(保证 EVALSHA 成功被调用)明确支持这种情况。


Redis 大量数据插入


pipe mode的工作原理是什么?

难点是保证redis-cli在pipe mode模式下执行和netcat一样快的同时,如何能理解服务器发送的最后一个回复。
这是通过以下方式获得:

redis-cli –pipe试着尽可能快的发送数据到服务器。
读取数据的同时,解析它。
一旦没有更多的数据输入,它就会发送一个特殊的ECHO命令,后面跟着20个随机的字符。我们相信可以通过匹配回复相同的20个字符是同一个命令的行为。
一旦这个特殊命令发出,收到的答复就开始匹配这20个字符,当匹配时,就可以成功退出了。

同时,在分析回复的时候,我们会采用计数器的方法计数,以便在最后能够告诉我们大量插入数据的数据量。


attachments-2020-08-o9ycXsOI5f476bda945c8.jpg

  • 发表于 2020-08-27 16:18
  • 阅读 ( 673 )
  • 分类:数据库

你可能感兴趣的文章

相关问题

0 条评论

请先 登录 后评论
Pack
Pack

1135 篇文章

作家榜 »

  1. 轩辕小不懂 2403 文章
  2. 小柒 1316 文章
  3. Pack 1135 文章
  4. Nen 576 文章
  5. 王昭君 209 文章
  6. 文双 71 文章
  7. 小威 64 文章
  8. Cara 36 文章