11 key 相关操作

Posted 蓝风9

tags:

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

前言

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

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

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

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

DEL key - 执行 del name 

这里可以看出的是 参数的数量要求大于等于 2 个, 也就是可以删除多个 key 

del 操作是一个对于 key 的写操作 

先看一下 del 的执行的处理 

循环传入的多个 key, 进行 同步/异步 删除, 更新 dirty, 更新 numdel 

最终返回给客户端 删除的元素的数量 

再看一下 c->argv[1], 这里表示的是传入的 "name", 封装成了一个 robj 

type 为 0, 表示的是 OBJ_STRING, encoding 为 8 表示的是 OBJ_ENCODING_EMBSTR 

lru 记录的是一个上次使用的时间[不精确, 可以标记时间, 默认情况下会以秒迭代增加] 

refcount 表示引用 robj 的数量 

ptr 表示的是 robj 对应的字符串的 sds 的地址信息, 因为 encoding 是 OBJ_ENCODING_EMBSTR 

因此 ptr 指向的是 robj 对象之后的地址, sizeof(robj) 为 16, prt 指向的也就是这里的 0x7f9d9e100533(sds的地址), sdshdr 在 0x7f9d9e100533 

inspect 一下 c->argv[1]  

type 四bit : 0x0 

encoding 四bit : 0x8 

lru 二十四bit : 0x497a94 = 4815508 

refcount 4字节 : 0x01 

ptr 8字节 : 0x7f9d9e100533 

(lldb) x 0x7f9d9e100520
0x7f9d9e100520: 80 94 7a 49 01 00 00 00 33 05 10 9e 9d 7f 00 00  ..zI....3.......
0x7f9d9e100530: 04 04 01 6e 61 6d 65 00 03 00 00 00 00 00 02 00  ...name.........

prt 指向的是 0x7f9d9e100533, 从上面我们可以分析出是一个 sds 对象 

首先看一下 0x7f9d9e100532, 0x01 是 flags, 表示的是这个 sds 的 sdshdr 为 sdshdr8, 那么 [0x7f9d9e100530 - 0x7f9d9e100532] 均是 sdshdr8 的内容 

那么可以得到 len 为 4, alloc 为 4 

从 0x7f9d9e100533 开始的连续四个字节为 这个对象的数据 "name"

删除给定的 key 对应的 entry 的具体处理 

从 db->expires 删除 key 的过期信息, 从 db->dict 里面删除 key 对应的 entry 信息 

发送相关通知信息 

删除之前 db->dict 里面有一个 entry : name -> jerry 

删除之后 db->dict 里面 name 对应的 entry 是被删掉了 

最终客户端这边展示的结果如下, 展示的是 被删除元素的数量 

DUMP key - 执行 dump name 

这里可以看出的是 参数的数量要求等于 2 个, dump 之后会跟一个 key 

看一下 dumpCommand 

查询是否存在 key 对应的 entry, 如果不存在, 直接给客户端返回 NULL 

否则 dump 给定的对象, 然后返回 

从上下文可以看到我们传入的 key 为 "name", name 对应的 value 为 "jerry", 类型为 OBJ_STRING, encoding 为 OBJ_ENCODING_EMBSTR 

我们来看看如何 dump 这个对象的 

整个结果由四部分组成 : 对象类型, 对象的数据信息, RDB_VERSION, crc 四部分 

我们这里只简单剖析 OBJ_STRING 是如何 dump 的, 其他的几种类型 请自行阅读/调试源码 

我们 inspect 一下 payload->io.buffer.ptr 

sds 编码为 sds8, len 为 0x11 = 17, alloc 为 0x2c = 48, 后面的 17 字节为 payload->io.buffer.ptr 的数据 

00 表示的是 RDB_TYPE_STRING 

05 表示的是 "jerry" 的长度 

6a 65 72 72 79 表示数据 "jerry" 

09 00 表示的是 RDB_VERSION 的低字节 和 高字节, 我这里为 9 

23 d2 cf 8e 25 60 70 c2 表示的是前面的内容的 crc, 合计 8 字节 

总共内容 合计 1 + 1 + 5 + 2 + 8 = 17 字节 

(lldb) x 0x7f9d9c42bfd0
0x7f9d9c42bfd0: 11 2c 01 00 05 6a 65 72 72 79 09 00 23 d2 cf 8e  .,...jerry..#��.
0x7f9d9c42bfe0: 25 60 70 c2 00 00 00 00 00 00 00 00 00 00 00 00  %`p�............

最终客户端这边展示的结果如下(根据是否可以显示做了一些特殊的处理, 不可显示字节使用的约定的 \\xnn 来表示) 

EXISTS key - 执行 exists name 

这里可以看出的是 参数的数量要求大于等于 2 个,  之后可以跟 多个key 

至于具体的语义, 我们可以参见文档 或者接下来 调试代码  

我们来看一下 existsCommand 

遍历 keyList, 查询 key 是否存在, count 表示的是 存在的 key 的数量 

最终客户端这边展示的结果如下, 展示的是 keyList 中 key 存在的数量 

EXPIRRE key seconds - 执行 expire name 100

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

expire, expireAt, pExpire, pExpireAt 都是差不多, 这里只介绍一个 

我们来看一下 expireCommand, 这里传入的 basetime 为 now(), unit 为 SECONDS 

从 argv[2] 获取偏移的 秒数/毫秒数, 统一适配成毫秒 offsetInMs

基于 basetime 和 offsetInMs, 计算 when[什么时候过期] 

校验 when 是否合法, 如果 when 不合法, 直接返回 错误 "invalid expire time in $cmdName" 

如果 key 对应的 entry 不存在, 直接返回 0 

如果 when 小于当前时间, 删除 key 对应的 entry, 并发送删除 key 相关通知, 返回 1 

设置过期时间[db->expires], 返回 1, 发送 更新 key 相关通知, 更新 dirty 

最终客户端这边展示的结果如下, 设置成功 返回的是 1 

KEYS pattern - 执行 keys nam*

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

我们来看一下 keysCommand 

通过迭代器遍历 c->db->dict, 判断 key 是否匹配给定的 pattern, 如果匹配 增加key返回 

会预插入一个 len 在匹配的 keyList 之前, 最后来更新 

这里匹配模式基于 glob 表达式, stringmatchlen 做的具体的实现 

最终客户端这边展示的结果如下, 返回的是 "nam*" 匹配的一个 keyList 

MOVE key db - 执行 move name 2

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

我们来看一下 moveCommand 

先获取 srcDb, srcId, dstDb, dstId 

判断 srcDb 中 key 是否存在, 不存在直接返回 错误信息 

选择 dstDb, 如果目标 db 不存在, 直接返回错误信息 

如果 srcDb 和 dstDb 相同, 返回 sameobjecterror 的错误 

如果 srcDb 中不存在 key 对应的 entry, 返回 0 

如果 dstDb 中已经存在 key 对应的 entry, 返回 0 

在 dstDb 中添加 key 对应的 entry, 并设置过期时间 

从 srcDb 中删除 key 对应的 entry 

发送 srcDb key 的更新通知, dstDb key 的更新通知 

更新 dirty, 返回 1 

最终客户端这边展示的结果如下, 返回的是 移动 name 对应的 entry 到 dbIdx 为 2 的 redisDb 成功 

PERSIST key - 执行 persist name

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

我们来看一下 persistCommand 

如果 key 对应的 entry 不存在, 返回 0 

如果 key 对应的 entry 本身就永不过期, 返回 0 

移除 key 对应的 entry 的过期时间, 发送 c->db key 的更新通知, 返回 1, 更新 dirty 

最终客户端这边展示的结果如下, name 是存在过期时间, 这里移除了过期时间, 显示的 1 

TTL key - 执行 ttl name 

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

ttl, pttl 都是差不多, 这里只介绍一个 

我们来看一下 ttlCommand 

如果 key 对应的 entry 不存在, 返回 -2 

获取 key 对应的 entry 的过期时间, 计算剩余的 ttl[time to live, 毫秒计算] 

如果 永不过期, 返回 -1 

否则 适配秒/毫秒 返回结果信息 

最终客户端这边展示的结果如下, name 对应的 entry 还有 98 秒的生存时间 

RANDOMKEY - 执行 randomkey 

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

我们来看一下 randomkeyCommand 

使用 dict 的随机算法, 获取一个随机的 key, 如果无元素 返回 null 

否则 返回这个 随机的 key 

最终客户端这边展示的结果如下, db 0里面一个元素 "name", 随机那就是 "name", 返回的是 "name" 

RENAME key newKey - 执行 rename name newName

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

rename, renamenx 都是差不多, 这里只介绍一个 

我们来看一看 renameCommand 

如果 key 不存在, 返回 nokeyerr 

如果 name 和 newName 相同返回 OK[取决于是否是 nx] 

如果 newKey 对应的 entry 存在, 则删除 newKey 对应的 entry(nx 操作, 直接返回 0)

增加 newKey 对应的 entry, 值为 key 对应的 entry.value 

删除 key 对应的 entry 

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

最终客户端这边展示的结果如下, 更新 name 对应的 entry 对应的key为 newName 成功 

SCAN cursor [MATCH pattern] [COUNT count] - 执行 scan 0 MATCH * COUNT 20 TYPE string

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

我们来看一下 scanCommand 

如下部分是解析 scan 的命令的相关参数, 解析 match 的 pattern 为 *, count 为 20, type 为 string 

我们这里 o 为 NULL, 因此 ht 为 c->db->dict, 然后基于 dictScan 来进行 scan 的处理, 这里会基于 count 迭代采集 keys 

然后根据 pattern, type 来过滤给定的 keys 

最终将符合条件的 keys[dict 可能包含 value], 返回给客户端 

最终客户端这边展示的结果如下, 返回的是下一次迭代的 cursor 和 当前批次迭代的 keyList 

TYPE key - 执行 type newName

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

我们来看一下 typeCommand 

获取 newName 对应的 entry, 从 type 中获取对应的类型的字符串表示 返回, 这里 newName 对应的 entry 的类型为 string 

根据 o->type 获取对应的高级数据结构的字符串表示  

最终客户端这边展示的结果如下, 输出的是 newName 对应的 entry 的高级数据结构的类型, 这里是 string 

完 

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

spark关于join后有重复列的问题(org.apache.spark.sql.AnalysisException: Reference '*' is ambiguous)(代码片段

VSCode自定义代码片段——git命令操作一个完整流程

Python snippet(代码片段)

使用jedis操作redis

Map集合中,关于取值和遍历的相关操作

Android:片段内的按钮操作