文章目录
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 日才刚刚写完,深感愧疚……
- 年轻就应该过年轻的生活,如果我十岁不幼稚,二十岁不莽撞,三十岁不自负,四十岁不自省,五十岁不感恩,六十岁不天真,我觉得这都是人生的错误。
- 当你想做出改变时,与其与固有的事物对抗,不如用新的事物去淡化它,去稀释它,最终让固有事物消失得无影无踪。
- 成年人也是过期的小朋友。