15 set 相关操作

Posted 蓝风9

tags:

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

前言

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

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

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

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

数据结构

如果 value 均可以使用 longlong 来表示, 则使用 intset 来存储数据 

否则使用 dict 来存储数据 

SADD key member1 [member2] - 执行 sadd set name age

这里可以看出的是 参数的数量要求大于等于 3 个, 可以传递 多个 member  

我们来看一下 saddCommand 

获取 key 对应的 entry, 确保类型为 set 

如果 key 对应的 entry 不存在, 则根据 member1 创建 set[如果可以 cast longlong, 创建 intset, 否则创建 dict]

然后 依次往 value 中添加元素(如果当前编码是 intset, 来了一个 不能 cast longlong 的元素, 转换当前 value 的存储方式, 更新为 dict) 

更新 dirty, 发送 更新通知, 返回添加的元素的数量 

set 中添加元素的具体处理 

最终客户端这边展示的结果如下, set 不存在, member1 为 name, 因此创建的是 dict, 然后 往 dict 里面添加了 name, age 作为元素 

最终返回的是 value 中新增的元素的数量, 新增了两个 

SCARD key - 执行 scard set

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

我们来看一下 scardCommand 

获取 key 对应的 entry, 确保类型为 set 

获取 set 中元素的数量 并返回给客户端 

set 中获取元素数量的具体的方式 

最终客户端这边展示的结果如下, set 中存在 name 和 age 两个元素, 因此 scard 返回的结果为 2 

SDIFF key1 [key2] - 执行 sdiff set set2

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

sdiff, sdiffscore, sunion, sunionstore 都是差不多, 这里只介绍一个[使用的同一个工具方法, 但是算法是分开的] 

我们来看一下 sdiffCommand 

取出所有的 set, 如果某一个 set 类型不匹配, 校验异常
如果是求差集, 计算所计算的集合列表的基数 score, 差集算法1关心的是 set[0] 的基数, 差集算法2关心的是 所有set 的基数[最后如果决策时算法1, 对后面的集合基于基数排序, 提高比较效率]
如果是 并集计算, 创建一个 set, 遍历所有的 set, 将数据添加到结果 set 中
如果是 差集计算, 采取的是算法1, 遍历 set[0] 的元素, 然后 二级遍历 set[1], ..., set[n] 如果给定的元素在后面的所有的 set 都没有出现, 将其添加到 resultSet 里面
如果是 差集计算, 采取的是算法2, 将 set[0] 的所有元素添加到 resultSet, 遍历后面的所有的集合, 如果已经在 resultSet 中出现, 将其移除
最后是一些业务上的处理, 输出数据到 客户端 或者 unionScore, diffScore 的相关计算

最终客户端这边展示的结果如下, set 中存在 name 和 age 两个元素, set2 这个集合不存在, 因此 sdiff 最终计算出来结果为 [name, age]

SINTER key1 [key2] - 执行 sinter set dstset 

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

sinter, sinterscore 都是差不多, 这里只介绍一个 

我们来看一下 sinterCommand 

如果某一个集合不存在, 校验不通过
如果某一个集合 不为 set, 校验不通过
根据 set 列表基数排序, size 小的排在前面
以 set[0] 作为基准, 和后面的 set[n] 对比
    如果s[0]编码为 int, 并且对比 set[n] 编码为 ineset, 在 set[n] 中查找, 编码为 hashtable 在 set[n] 中查找
    如果s[0]编码为 hashtable, 如果 ele.type 为 int, 并且 set[n] 编码为 int, 则使用 intset 查询, 否则使用 hashtable 查询
如果 n 个集合都有元素 ele, 则将其封装到 result 里面
最终返回 result

最终客户端这边展示的结果如下, set 中存在 name 和 age 两个元素, dstset 中存在 name 和 age 两个元素, set 和 dstset 的交集为 [name, age] 

SISMEMBER key member - 执行 sismember set age

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

我们来看一下 sismemberCommand 

获取 key 对应的 entry, 确保类型为 set 

查询 value 对应的 Set 中是否有传入的值 


set 中查询给定的元素是否存在 

最终客户端这边展示的结果如下, set 中存在 name 和 age 两个元素, 查询 set 中是否包含 age 这个元素, 这里是包含, 返回给客户端的是 1 

SMEMBERS key - 执行 smembers set

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

smembers 是基于 sinter 来实现的, 这里不多赘述 

最终客户端这边展示的结果如下, set 中存在 name 和 age 两个元素, 这里返回给客户端的为 [age, name]

SMOVE source destination member - 执行 smove dstset set name

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

我们来看一下 smoveCommand 

如果源集合 目标集合 类型不匹配, 直接返回
如果源集合 和 目标集合相同, 直接返回
从源集合中删除给定的元素, 如果给定的元素不存在, 直接返回(如果源集合删除元素之后为空, 删除源集合)
如果目标集合不存在, 创建目标集合
将元素添加到 目标集合

最终客户端这边展示的结果如下, set 中存在 name 和 age 两个元素, dstset 中存在 name 和 age 两个元素, 将 dstset 中的 name 移动到了 set 里面 

因此 操作之后 dstset 中只存在元素 age 一个元素, set 中存在 name 和 age 两个元素

SPOP key [count] - 执行 spop set 

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

我们来看一下 spopCommand 

如果携带了 count 参数, 单独根据 count 以及 集合的情况特殊处理 

获取 key 对应的 entry, 确保类型为 set 

随机从 set 中获取一个元素, 并将改元素从 set 中移除 

返回改元素给客户端, 重写 spop 为 srem $ele 

更新 dirty, 发送 更新通知 

对于 pop count 个元素的处理 

如果 count 大于集合的元素总数, 业务委托给 sunion, 向客户端写出数据等等 

之后删除掉 key 对应的 entry, 命令传播为 "del key", 发送更新通知 

假设 count 远小于 remaining, 则循环随机 获取 count 个元素返回给客户端, 并从集合中删除该 count 个元素 

否则 新建一个新的集合, 来随机挑选 remaining 个元素 添加到新集合, 并从已有的集合移除该元素, 返回 已有的集合中剩余的元素[random 的 count 个元素], 更新 key 对应的 value 为 新集合 

最终客户端这边展示的结果如下, set 中存在 name 和 age 两个元素, 随机移除了一个元素 age 

呵呵 注意这里的 pop 和普通的语义是有差异的, 因为 set 是没有顺序的, 可能这里是 随机 pop 一个元素[个人理解] 

SRANDMEMBER key [count] - 执行 srandmember set 

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

我们来看一下 srandmemberCommand 

这里的策略 和上面的 spop 的策略是基本上一致的, 基于 dict, intset 的随机获取元素的算法, 这里不多赘述 

只是相比于 spop 这里 只会随机获取元素, 不会移除给定的元素  

最终客户端这边展示的结果如下, set 中存在 name 一个元素, 随机获取了一个元素 name 

SREM key member1 [member2] - 执行 srem set name

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

我们来看一下 sremCommand 

遍历 memberList, 依次从 set 中删除给定的 member, 记录删除的元素数量 

如果删除了 n 个元素, 更新 dirty, 发送 更新通知 

最终客户端这边展示的结果如下, set 中存在 name 一个元素, 然后我们这里删除了 name, 删除了一个元素 

SSCAN key cursor [MATCH pattern] [COUNT count] - 执行 sscan key 0  

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

这的迭代类似于 11 key 相关操作 

不过对于这里编码为 intset 的情况 COUNT 这两个选项是没用的[这是一个很细的细节] 

迭代的是 set 里面的所有的元素  

最终客户端这边展示的结果如下, set 这个 entry 下面所有的 匹配 pattern 的 member 列表 

完 

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

集合相关操作函数

set,map相关操作

set相关操作总结(待续)

Set,Sorted Set相关命令操作,批量插入及管道,事务

用java代码操作Redis进行相关的Hash,String,Set,List操作

存入MySQL的相关操作与配置