12 string 相关操作

Posted 蓝风9

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了12 string 相关操作相关的知识,希望对你有一定的参考价值。

前言

相关介绍主要围绕着如下的一些常用的命令, 来看看 字符串 相关操作的具体 api 

如下常用的命令来自于我们常见的教程 : https://www.runoob.com/redis/redis-strings.html 

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

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

数据存储

string 数据的存储是基于 sds 来进行存储的 

SET key value - 执行 set name jerry ex 100 xx 

这里可以看出的是 参数的数量要求大于等于 3 个, set 里面又诸多选项, 我们接下来看 

我们来看一看 setCommand 

两步走, 首先是解析 输入命令中的各种标记 

接着是执行 set 的相关实际业务处理 

flags 中标记了相关的一些特性, 诸如是否 传入了 NX, XX, SET_GET, KEEPTTL, EX, PX, EXAT, PXAT 等等 options 

expire 表示的是传入的过期时间, unit 表示过期时间的单位 

对于传入参数列表的解析 

这里可以直接看上面的 "规范", 说的很明晰 

只有 GET 可用的选项 : PERSIST, DEL 

只有 SET 可用的选项 : XX, NX, GET 

SET, GET 都可以用的选项 : EX, EXAT, PX, PXAT, KEEPTTL 

如果参数不符合规范, 给客户端返回 语法错误  

下面的代码是上面的 "规范" 的实现而已 

关于 flags 

flags 各个标记位对应意义如下, 我们这里 flags 为 6, 表示 SET_XX, EX 标记为 true, 用户传入了这两个 options 

我们来看一下 set 的具体的业务的实现 

获取 ttl, 计算 key 对应的 entry 的过期时间 when 

如果是有 NX 选项, 并且 key 对应的 entry 存在, 返回 null 

如果是有 XX 选项, 并且 key 对应的 entry 不存在, 返回 null 

如果是有 SET_GET 选项, 并且 key 对应的 entry 不存在, 返回 null 

c->db->dict 中保存 key -> value 对应的 entry, 如果是需要 KEEPTTL, 移除过期时间, 更新 dirty

如果传入了 ttl, 设置 key 对应的 entry 的过期时间, 返回 OK  

最终客户端这边展示的结果如下, 展示的是 set 的结果 reply 的是 null 

GET key - 执行 get name 

这里可以看出的是 参数的数量要求等于 2 个 

我们来看一下 getCommand 

获取 key 对应的 entry, 如果不存在, 直接返回 null 

确保 o 的类型为 OBJ_STRING, 类型不匹配抛出 wrongtypeerr 

给客户端输出 o 的数据信息, 这里为 "jerry" 

最终客户端这边展示的结果如下, 展示的是 name 对应的 entry 的 value 为 "jerry"

GETRANGE key start end - 执行 getrange name 1 3

这里可以看出的是 参数的数量要求等于 4 个 

我们来看一看 getrangeCommand 

从客户端传递过来的参数中获取 key, start, end 如果格式不正确, 或者 key 存在问题, 或者类型存在问题, 返回给客户端错误信息 

获取存储的字符串的信息, 以及字符串的长度  

校验 start, end 索引是否合法, 不合法 给客户端响应 emptybulk 

对于 start, end 为负的约定进行真实索引计算, 如果还不合法 补偿更新为 0, 如果 end 太大 补偿更新为字符串末尾 

返回 key 对应的 value 的 substring(start, end), 包含 start, end 响应给客户端 

最终客户端这边展示的结果如下, 展示的是 name 对应的 entry 的 value 为 "jerry" 的 [1, 3] 子串 "err" 

GETSET key value - 执行 getset name jerry.he

这里可以看出的是 参数的数量要求等于 3 个 

我们来看一下 getsetCommand 

首先是 读取 key 对应的 entry 响应个客户端 

然后设置 key -> newValue 到 c->db->dict, 更新 dirty 

后面的重写 c->argc, c->argv, 将传递的 getset name jerry.he 更新为 set name jerry.he, 可能这是组合的命令, 将它归为了 set 系列操作, 后面的 context 可能需要读取参数做其他的业务处理吧 

最终客户端这边展示的结果如下, 展示的是 name 对应的 entry 的 value 为 "jerry", 然后将 name 对应的 value 设置成了 "jerry.he" 

我再吧 name 对应的 value 设置回 jerry 

GETBIT key offset - 执行 getbit name 6 

这里可以看出的是 参数的数量要求等于 3 个 

我们来看看 getbitCommand 

从参数中获取 偏移, 如果格式不正确 响应异常信息给客户端 

获取 name 对应的 entry, 并校验类型, 格式不正确 响应异常信息给客户端 

通过 bitOffset 来计算属于哪一个字节 byte, 以及这一个字节的哪一个 bit 

获取第 byte 字节的第 bit 位的数据, 是 0 还是 1, 直接返回 

呵呵 这里支持的按位系列操作, string 就是一个天然的 bitmap 

最终客户端这边展示的结果如下, 展示的是 name 对应的 entry 的 value 为 "jerry", 的第 6 bit, "je" 存储的是 "0b0110 1010 0110 0101", 第 6bit 为1, 即为这里客户端接收到的结果 

我们来看一下 这里的 第一个 byte 的所有的数据, 确实是我们上面期望的 "j" 存储的数据 "0b0110 1010"

SETBIT key offset value - 执行 setbit name 3 1 

这里可以看出的是 参数的数量要求等于 4 个 

我们来看一下 setbitCommand 

同样的先定位是那一字节 byte, 在定位是这一字节的那一位 bit 

然后 在内存中更新 这一位 

我们这里 name 对应的 value 为 "jerry" 

"j" 为 "0b0110 1010", 我们这里将第三个 bit 更新为 1, 更新之后为 "0b0111 1010"

"0b0111 1010" 对应于十进制为 122, 为 "z", 因此从 value 上来看, "jerry" 更新成了 "zerry"

SETRANGE key offset value - 执行 

这里可以看出的是 参数的数量要求等于 3 个 

我们来看下 setrangeCommand 

获取 offset, 并校验, 不合法 响应错误信息给客户端 

获取 key 对应的 entry, 如果不存在, 如果 传入value 为空, 直接返回, 否则 创建 robj, 并添加到 c->db->dict 里面 

如果 key 对应的 entry 存在, 确保类型为 STRING, 如果 传入value 为空, 直接返回, 确保 更新之后的 size 合法 

更新 value 的长度, 确保 在offset处 能够存储 传入value, 复制 传入value 到 offset 处, 更新 dirty 

返回 value 更新之后的长度 

最终客户端这边展示的结果如下, 展示的是 name 对应的 entry 的 value 为 "jerry", 的第三byte之后的 6byte 更新为 "rrrrrr", 返回的是 9 表示的是 "jerrrrrrr" 的长度 

更新之后 name 的值为 "jerrrrrrr"

STRLEN key

这里可以看出的是 参数的数量要求等于 2 个 

我们来看看 strlenCommand 

获取 key 对应的 entry, 并确保类型为 STRING, 如果不合法 响应错误信息给客户端 

获取 key 对应的额 value 的length[如果是字符串编码, 直接获取长度, 如果是整形编码, 通过整形值计算所需长度] 

最终客户端这边展示的结果如下, 展示的是 name 对应的 entry 的 value 为 "jerrrrrrr" 的长度为 9 

MSET key value [key value ...] - 执行 mset name jerry age 77 

这里可以看出的是 参数的数量要求大于等于 3 个 

mset, msetnx 都是差不多, 这里只介绍一个 

我们来看一下 msetCommand 

其实就是一顿快捷操作, 设置多个 key value 对 

更新 dirty 

最终客户端这边展示的结果如下, 展示的是 设置了 name -> jerry, age -> 77 成功 

MGET key [key...] - 执行 mget name age 

这里可以看出的是 参数的数量要求大于等于 2 个

我们来看一下 mgetCommand 

响应给客户端的数据为一个数组, 先响应了长度  

然后再逐步响应了 keyList 中各个 key 对应的 value 的信息 

最终客户端这边展示的结果如下, 展示的是 name 和 age 的结果列表  

SETEX key seconds value - setex name 100 jerry

这里可以看出的是 参数的数量要求等于 4 个 

setex, psetex 都是差不多, 这里只介绍一个 

我们来看看 setexCommand 

是基于 setGenericCommand 来处理的业务, 这里不再赘述 可以参见 "SET key value" 

最终客户端这边展示的结果如下, 展示的是 设置了 name -> jerry ttl 为 100s 

INCR key - 执行 incr counter

这里可以看出的是 参数的数量要求等于 2 个 

incr, decr, incrBy, decrBy 都是差不多, 这里只介绍一个 

我们来看一下 incrCommand 

根据 key 获取 entry 的数据, 校验类型为 OBJ_STRING 

获取 key 对应 value 的值, 如果不能转换为 long long, 响应错误信息给客户端 

如果可能存在 溢出的可能, 抛出错误信息 

value 累计增加 incr 

更新 key 对应的 entry 的 value 的值为更新之后的值 

更新 dirty, 给客户端返回 累增 之后的结果 

最终客户端这边展示的结果如下, 之前 不存在 counter 这个 entry, incr 之后 counter 为 1, 客户端的接受到的结果为 1 

APPEND key value - 执行 append name .x.he

这里可以看出的是 参数的数量要求等于 3 个 

我们来看一下 appendCommand 

获取 key 对应的 entry, 如果不存 新增 key -> value 到 c->db->dict 

如果 key 对应的 entry 存在, sdscat 添加 value 到原有的 entry 的值上面 

更新 dirty, 发送 db key 的更新通知 

返回 更新之后的 value 的长度 

最终客户端这边展示的结果如下, name 的值为 "jerry", 然后我这里 append 了 ".x.he" 

append 之后 name 的值为 "jerry.x.he", 这里客户端展示的是 服务器返回的 "jerry.x.he" 的长度 为 10 

完 

以上是关于12 string 相关操作的主要内容,如果未能解决你的问题,请参考以下文章

大数据之Redis:Redis数据类型String及相关的命令行操作

Redis 数据结构操作相关命令

Redis 数据结构操作相关命令

Redis01​Redis基础:String相关操作

轻量数据库SQLiteDataBase的相关操作方法

数组相关操作