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 相关操作的主要内容,如果未能解决你的问题,请参考以下文章