前言

关于 redis 的一些基本的代码层面上的约束 

关于一些规范中介绍的东西, 我们这里可以从代码层面上来直观的了解这些东西 

本文的相关代码 拷贝自 redis-6.2.0  

代码来自于 https://redis.io/ 

高级数据结构

这里的数据类型指的是给用户用到的这些数据类型, 也就是我们常用的 STRING, HASH, LIST, SET, SORTEDSET 

Redis provides data structures such as strings, hashes, lists, sets, sorted sets with range queries

几种客户端使用的数据结构 : 字符串, 列表, 集合, 有序集合, 哈希  

10 基本说明_数据

低级数据结构

几种支持高级数据结构的底层数据结构 : 原始指针, 整形, dict, zipmap, linkedlist, ziplist, intset, skiplist, embedded string, quicklist 

10 基本说明_数据_02

高级数据结构和低级数据结构之间的关系

字符串 : sds 

哈希 : ziplist, dict 

列表 : quicklist 

集合 : intset, dict 

有序集合 : ziplist, skiplist 

10 基本说明_数据_03

高级数据结构低级数据结构映射表格

高级数据结构

数据结构1

数据结构1条件

数据结构2

数据结构2条件

字符2

sds

true

哈希

ziplist

len <= hash_max_ziplist_entries (默认为512)

dict

!ziplist 

列表

quicklist

true

集合

intset

value could cast longlong 

dict

!intset

有序集合

ziplist

len < zset_max_ziplist_entries(默认为128)

len(value) < zset_max_ziplist_value(默认为64)

dict

!ziplist

shared 对象

这里主要是一些 享元, 业务中比较常用的一些东西, 使用的同一个对象(不可变)

10 基本说明_数据_04

各个命令以及配置信息的初始化 

配置的是命令的相关元素据信息, 命名名称, 命令处理函数, 参数的约束, 一些其他约束 

每一个 part 的数据, 都可以看上面的注释的相关的信息 

10 基本说明_数据_05

各个配置项的初始化

各个配置项 别名, 是否修改, 默认值, 配置合法的校验, 更新配置的校验 

10 基本说明_constants_06

关于网络交互

基于 connection 从客户端读取数据

10 基本说明_constants_07

基于 connection 向客户端读取数据

下面的一系列 high level api, low level api, 都是为了吧 业务的数据写出到 c->buf, c->reply 里面暂存起来 

那么什么时候 把数据交互给客户端这边呢? 

writeToClient 这里面实现了 基于 c->connection 写出 c->buf, c->reply 里面的数据到 客户端 

10 基本说明_config_08

addReply 系列相关

addReply / addReplySds / addReplyProto 

这几个都是 业务这边经常会使用到的一些 high level functions 

优先将数据添加到 buf 里面 

如果 buf 空间不够 或者 已经使用 reply 列表, 则将数据添加到 reply 列表 

10 基本说明_redis_09

addReplyErrorObject / addReplyError / addReplyErrorSds / addReplyErrorFromat / addReplyErrorLength 

添加错误信息 返回给客户端, 格式为 

-ERRORCODE Error Message<CR><LF>

10 基本说明_constants_10

10 基本说明_数据_11

addReplyStatusLength / addReplyStatus / addReplyStatusFormat 

返回状态信息给客户端, 格式为 

+Status Message<CR><LF>

10 基本说明_redis_12

addReplyDeferredLen / setDeferredReply / setDeferredAggregateLen / setDeferredArrayLen / setDeferredMapLen / setDeferredSetLen / setDeferredAttributeLen / setDeferredPushLen

addReplyDeferredLen : reply 列表中新增一个 dummy 节点, 来占位 

setDeferredReply : 查看 next 节点是否有足够的空间来存储 [s, s+len-1], 如果有 将 next 的已有的数据向后移动 len, 复制 [s, s+len-1] 到 next 头部, 移除 dummy 节点 

                                       否则新建一个 replyBlock, 来存储 [s, s+len-1] 的数据, 将 replyBlock 关联到 dummy 节点上面[不再是 dummy 节点了]

setDeferredAggregateLen : 添加指定前缀 + [s, s+len-1] 的内容到 reply 列表中 

在 protocol 2 中, 长度均使用 "*" 

在 protocol 3 中, ArrayLength 使用 "*", MapLength 使用 "%", SetLength 使用 "~", AttributeLength 使用 "|", PushLength 使用 ">" 

10 基本说明_数据结构_13

10 基本说明_redis_14

10 基本说明_数据结构_15

addReplyDouble / addReplyHumanLongDouble 

addReplyDouble : 如果 d 是 infinite, 根据 protocol 做不通的响应, 大致的内容为 "inf", "-inf" 

                              如果是 protocol2, 添加 "\$$len\r\n$data\r\n" 到 buffer 

                              如果是 protocol3, 添加 ",$data\r\n" 到 buffer 

addReplyHumanLongDouble : 如果是 protocol2, 添加 "\$$len\r\n$data\r\n" 到 buffer 

                              如果是 protocol3, 添加 ",$data\r\n" 到 buffer 

10 基本说明_constants_16

addReplyLongLongWithPrefix / addReplyLongLong / addReplyAggregateLen / addReplyArrayLen / addReplyMapLen / addReplySetLen / addReplyAttributeLen / addReplyPushLen 

addReplyLongLongWithPrefix : 如果 prefix 是 "*", ll 位于 [0, OBJ_SHARED_BLUKHDR_LEN], 使用 共享变量 "*$ll" 到 buffer 

                                                   如果 prefix 是 "$", ll 位于 [0, OBJ_SHARED_BLUKHDR_LEN], 使用 共享变量 "\$$ll" 到 buffer 

                                                   添加 "$prefix\r\n$data\r\n" 到 buffer 

addReplyLongLong : 如果是 0, 1 使用共享变量, 否则添加 ":$ll" 到 buffer 

addReplyAggregateLen / addReplyArrayLen / addReplyMapLen / addReplySetLen / addReplyAttributeLen / addReplyPushLen : 就是根据协议选择前缀 + 数据添加到 buffer 

10 基本说明_数据_17

10 基本说明_数据_18

addReplyNull / addReplyBool / addReplyNullArray 

addReplyNull : 如果是 protocol2, 添加 "$-1\r\n" 到 buffer, 如果是 protocol3, 添加 "_\r\n" 到 buffer 

addReplyNullArray : 如果是 protocol2, 添加 "$-1\r\n" 到 buffer, 如果是 protocol3, 添加 "_\r\n" 到 buffer 

addReplyBool : 如果是 protocol2, 添加 ":1", ":0" 到 buffer, 如果是 protocol3, 添加 "#t\r\n", "#f\r\n" 到 buffer 

10 基本说明_数据结构_19

addReplyBulkLen / addReplyBulk / addReplyBulkCBuffer / addReplyBulkSds / addReplyBulkString 

以 bulk 的格式来返回数据, 格式协议为 "\$$len\r\n$data\r\n" 

10 基本说明_redis_20

addReplyVerbatim 

如果是 protocol2, 直接以 bulk 的格式返回 "\$$len\r\n$data\r\n" 

如果是 protocol3, 添加 "=($len+4)\r\n$ext:$data\r\n" 到 buffer 

10 基本说明_constants_21

_addReplyToBuffer / _addReplyProtoToList 

写出给定的 [s, s+len-1] 到 buffer 或者 reply 

业务上调用的一系列的 addApplyXX 都是基于这里的 _addReplyToBuffer 或者 _addReplyProtoToList 

10 基本说明_config_22


 

一次命令交互的数据

我们以 如下命令为例 来进行抓包 

10 基本说明_config_23

请求概览

概览一下发现四个请求, 我们这里核心关注 交互数据的这两个请求[第一个 和 第三个]

第一个请求为 redis-cli 向 redis-server 发送的请求的数据 

第二个请求为 redis-server 向 redis-cli 发送的一个收到数据的 ack 包 

第三个请求为 redis-server 向 redis-cli 发送的响应的数据 

第四个请求为 redis-cli 向 redis-server 发送的一个收到数据的 ack 包 

10 基本说明_config_24

get name 的请求

前面的 tcp 协议元数据, ip 协议, ethernet 协议 这部分的数据 我们暂时不关注, 我们主要关注 tcp 协议传递的数据 

数据部分主要有 23 个字节, 内容为 "*2\r\n$3\r\nget\r\n$4\r\nname\r\n", 合计 23 bytes 

这个就是 redis-cli 这边 传递给 redis-server 的数据, redis-server 拿到数据之后封装到 c->querybuf 里面, 后面进而解析参数 放到 c->argc, c->argv 里面 

比如这里最终解析之后 c->argc 为 2, c->argv[0] 为 "get" 对应的 robj, c->argv[1] 为 "name" 对应的 robj 

10 基本说明_constants_25

get name 的响应

前面的 tcp 协议元数据, ip 协议, ethernet 协议 这部分的数据 我们暂时不关注, 我们主要关注 tcp 协议传递的数据 

数据部分主要有 11 个字节, 内容为 "$5\r\nzerry\r\n", 合计 11 bytes 

这个就是 redis-server 这边 传递给 redis-cli 的数据, redis-cli 拿到响应数据之后, 展示在 cmd 里面  

10 基本说明_redis_26