Redis 协议规范

1. 写在最前面

偶然间跟同事讨论起 redis 的 client 和 server 之间传输数据的协议是什么样子的,以 set hello world 为例:

我:set hello 5\r\nworld\r\n

同事 Z:*3\r\n$3\r\nset\r\n$5\r\nhello\r\n$5\r\nworld\r\n

但是这两种回答都是对的。因为偷懒的我阅读的源码是 1.3.6 的版本,但是勤奋的同事阅读的是 3.2 版本的源码。你看,就跟考试一样,有的时候你遇到的可能并非是一项单选题,而更多的时候其实是多选题哦。

既然这样,那不如顺手学习一下 redis 的协议规范 —— RESP (REdis Serialization Protocol)。

2. 试试手拼协议

本着「实事求是」的精深,用 go 写了一个简单的 redis-cli 的 client。代码的内容如下(ps 嗯,没错,就是 go

package main

import (
	"fmt"
	"net"
)

func main() {
    
    
	addr := "127.0.0.1:6379"
	tcpAddr, err := net.ResolveTCPAddr("tcp", addr)
	if err != nil {
    
    
		fmt.Errorf("resolveTCPAddr failed: %v", err)
		return
	}

	conn, err := net.DialTCP("tcp", nil, tcpAddr)
	if err != nil {
    
    
		fmt.Errorf("dail failed: %v", err)
		return
	}
  // 注:content3_2 为 3.2 版本的 redis-cli 的数据传入格式
	// content3_2 := "*3\r\n$3\r\nset\r\n$5\r\nhello\r\n$5\r\nworld\r\n"
  // 注:content1_3 为 1.3.6 版本的 redis-cli 的数据传入格式
	// content1_3 := "set hello 5\r\nworld\r\n"
  // 注:手动拼写 redis 协议,解决亿级别数据快速插入的问题,后面的部分会详细介绍(ps 此处可以不用关心
	contentBatch3_2 := "*3\r\n$3\r\nset\r\n$5\r\nhello\r\n$5\r\nworld\r\n*3\r\n$3\r\nset\r\n$5\r\nhelli\r\n$5\r\nworld\r\n*3\r\n$3\r\nset\r\n$5\r\nhellx\r\n$5\r\nworld\r\n"
	content := contentBatch3_2

	_, err = conn.Write([]byte(content))
	if err != nil {
    
    
		fmt.Errorf("Write to server failed: %v", err)
		return
	}

	fmt.Printf("write to server = %s \n", content)

	reply := make([]byte, 1024)

	_, err = conn.Read(reply)
	if err != nil {
    
    
		fmt.Errorf("read from server failed: %v", err)
		return
	}

	fmt.Printf("read from server = %s \n", reply)

	conn.Close()
}

2.1 试试 1.3.6 版本的数据插入

由下图截图可证,上述的手拼协议插入 1.3.6 版本完全可行

在这里插入图片描述

2.2 试试 3.2 版本的数据插入

由下图截图可证,上述的手拼插入 3.2 版本也是阔以的

在这里插入图片描述

2.3 思考 3.2 是否兼容了 1.3.6 的协议

要开始测试我用 1.3.6 的 redis 数据格式是否能够插入到 3.2 版本的 redis 中。

1.3.6 版本写入的内容为 "set hello 5\r\nworld\r\n"

但是在 3.2 版本的 server 中的解析实际上是分为两部分的:

  • set hello 5 为第一个 redis 指令
  • world 为第二条插入的指令

注:根据上述分析,答案应该是木有的,为什么呢?因为笔者看代码并没有看到兼容的地方 + 测下来就是不兼容的。

在这里插入图片描述

3. 如何快速插入亿级的键值对

显然 ping pong 一来一回的数据插入方式对于亿级键值对的数据插入必然会很慢。所以其实这里存在两种快速插入大量数据的方式:

  • Use the protocol, Luke

  • Generating Redis Protocol

具体的详细介绍可以见参考 Redis Mass Insertion

作为一个好奇宝宝,笔者一定是实践测试了的。

3.1 Use the protocol

这种方式就是把待插入的数据保存在一个文件中,然后用 --pipe 的方式插入,具体如下图所示:

在这里插入图片描述

3.2 Generating Redis Protocol

这种插入方式说白了就是在基于 redis 协议在一条消息体内写入多个 key value 的键值对(ps 拼写的消息格式 "*3\r\n$3\r\nset\r\n$5\r\nhello\r\n$5\r\nworld\r\n*3\r\n$3\r\nset\r\n$5\r\nhelli\r\n$5\r\nworld\r\n*3\r\n$3\r\nset\r\n$5\r\nhellx\r\n$5\r\nworld\r\n",具体如下图所示:

在这里插入图片描述

4. 碎碎念

好啦,横跨两个月的学习笔记终于写完了。记录下最近看到的惊艳自己的几句话吧。(ps 真从 11 月开始写,到 12 月 1 日才刚刚写完,深感愧疚……

  • 年轻就应该过年轻的生活,如果我十岁不幼稚,二十岁不莽撞,三十岁不自负,四十岁不自省,五十岁不感恩,六十岁不天真,我觉得这都是人生的错误。
  • 当你想做出改变时,与其与固有的事物对抗,不如用新的事物去淡化它,去稀释它,最终让固有事物消失得无影无踪。
  • 成年人也是过期的小朋友。

5.参考资料

Guess you like

Origin blog.csdn.net/phantom_111/article/details/110448874