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)(代码片段