237_Redis_数据类型_Set_Zset_Scan_Slot
1 SET 集合类型
相当于hashset 内部键值对无序且唯一 相当于一个特殊的字典,字典中所有的value 为 null
Redis的Set是string类型的无序集合。它底层其实是一个value为null的hashset ,所以添加,删除,查找的复杂度都是O(1)。
一个算法,随着数据的增加,执行时间的长短,如果是O(1),数据增加,查找数据的时间不变
1.1 应用场景:
需要存储一个列表数据,又不希望出现重复数据,set是一个很好的选择,并且set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list所不能提供的。
在微博应用中,可以将一个用户所有的关注人存在一个集合中,将其所有粉丝存在一个集合
活动抽奖 存储中奖用户的ID, 保证通过用户不会中奖两次
Redis还为集合提供了求交集、并集、差集等操作,可以非常方便的实现如共同关注、共同喜好、二度好友等功能,
对上面的所有集合操作,你还可以使用不同的命令选择将结果返回给客户端还是存集到一个新的集合中
1.2 常规操作1 增删改
# sadd将一个或多个成员元素加入到集合中,不能重复
# srem key value 用于移除集合中的一个或多个成员元素
# spop key 用于移除集合中的指定 key 的一个或多个随机元素常规操作2
# smove SOURCE DESTINATION MEMBER # 将指定成员 member 元素从 source 集合移动到 destination 集合
127.0.0.1:6379> SADD names alex bob
(integer) 2
127.0.0.1:6379> SREM names bob
(integer) 1
127.0.0.1:6379> SMOVE names names2 alex
127.0.0.1:6379> SMEMBERS names2
1) "alex"
2) "robbin"
127.0.0.1:6379> SMEMBERS names
(empty list or set)
1.3 常规操作2 查
# smembers返回集合中的所有的成员。
# sismember命令判断成员元素是否是集合的成员
# scard,获取集合里面的元素个数
# srandmember key 命令用于返回集合中的一个随机元素。
127.0.0.1:6379> SADD names alex bob
(integer) 2
127.0.0.1:6379> SMEMBERS names
1) "bob"
2) "alex"
127.0.0.1:6379> SCARD names
(integer) 2
127.0.0.1:6379> SRANDMEMBER names
"bob"
127.0.0.1:6379> SISMEMBER names alex
(integer) 1 # true
1.4 常规操作3数字集合类
- 差集: sdiff
- 交集: sinter
- 并集: sunion
127.0.0.1:6379> sadd set1 k1 k2 k3 k4
(integer) 4
127.0.0.1:6379> sadd set2 k2 k3 k5
(integer) 3
127.0.0.1:6379> SDIFF set1 set2
1) "k4"
2) "k1"
127.0.0.1:6379> sinter set1 set2
1) "k2"
2) "k3"
127.0.0.1:6379> SUNION set1 set2
1) "k1"
2) "k3"
3) "k2"
4) "k4"
5) "k5"
2 ZSet有序集合
类似java的SortedSet 和 Hashmap 的结合体, 一方面set保证唯一,另一方面它可以给 value 赋予一个score 代表这个value的排序权重 (score 可以重复)
素是有序的, 所以可以很快的根据评分(score)或者次序(position)来获取一个范围的元素
SortedSet(zset)是Redis提供的一个非常特别的数据结构
1 等价于Java的数据结构Map<String, Double>,可以给每一个元素value赋予一个权重score,
2 类似于TreeSet,内部的元素会按照权重score进行排序,可以得到每个元素的名次,还可以通过score的范围来获取元素的列表。
zset底层使用了两个数据结构
- hash,hash的作用就是关联元素value和权重score,保障元素value的唯一性,可以通过元素value找到相应的score值
- 跳跃表,跳跃表的目的在于给元素value排序,根据score的范围获取元素列表
跳跃表(跳表)
有序链表
跳跃表
从第2层开始,1节点比51节点小,向后比较。
21节点比51节点小,继续向后比较,后面就是NULL了,所以从21节点向下到第1层
在第1层,41节点比51节点小,继续向后,61节点比51节点大,所以从41向下
在第0层,51节点为要查找的节点,节点被找到,共查找4次。
可以看出跳跃表比有序链表效率要高 (6次)
2.1 应用场景:
排行榜应用,取TOP N操作; 成绩排名
这个需求与上面需求的不同之处在于,前面操作以时间为权重,这个是以某个条件为权重,比如按顶的次数排序,
sorted set,将要排序的值设置成sorted set的score,将具体的数据设置成相应的value
2.2 常用操作1 增 删
# zadd 将一个或多个成员元素及其分数值加入到有序集当中
# zrem 移除有序集中的一个或多个成员
127.0.0.1:6379> zadd studentscore 90 alex 80 bob 90 niuniu
127.0.0.1:6379> ZREM studentscore bob
(integer) 1
127.0.0.1:6379> ZRANGE studentscore 0 -1
1) "alex"
2) "niuniu"
2.3 常用操作2 查
# zrange 返回有序集中,指定区间内的成员
# zcard 命令用于计算集合中元素的数量。
# zcount 计算有序集合中指定分数区间的成员数量
127.0.0.1:6379> zadd studentscore 90 alex 80 bob 90 niuniu
(integer) 3
127.0.0.1:6379> ZRANGE studentscore 0 -1
1) "bob"
2) "alex"
3) "niuniu"
127.0.0.1:6379> ZCARD studentscore
(integer) 3
2.4 常用操作3 查排名
# zrangebyscore 返回有序集合中指定分数区间的成员列表。有序集成员按分数值递增(从小到大)次序排列
# zrank 返回有序集中指定成员的排名。其中有序集成员按分数值递增(从小到大)顺序排列。
# zrevrank 返回有序集中成员的排名。其中有序集成员按分数值递减(从大到小)排序。
127.0.0.1:6379> ZRANGEBYSCORE studentscore -inf +inf withscores
1) "bob"
2) "80"
3) "alex"
4) "90"
5) "niuniu"
6) "90"
127.0.0.1:6379> ZREVRANGE studentscore 0 -1 withscores
1) "niuniu"
2) "90"
3) "alex"
4) "90"
5) "bob"
6) "80"
127.0.0.1:6379> ZRANK studentscore alex
(integer) 1
127.0.0.1:6379> Zrevrank studentscore bob
(integer) 2
3 scan
原始方法 keys * / keys hello* #正则
缺点:
- 1 没有 offset , limit参数, 如果结果集很多 会刷屏
- 2 keys 遍历算法, 复杂度是O(n), 如果千万级key Redis 服务卡顿, 单线程读写redis指令都会延后或超时
解决 Redis 2.8版本 加入scan
- 1 复杂度还是O(n), 通过游标分步进行,不会阻塞线程
- 2 提供limit 参数,控制返回最大条数 limit只有一个hint,返回结果集可多可少
- 3 也提供正则 模糊匹配
- 4 结果集可能有重复,客户端去重
- 5 服务器不需要为游标保存状态,单次结果集为空不代表遍历结束,要看返回的游标值是否为 0
使用
SCAN cursor [MATCH pattern] [COUNT count]
# cursor 整数值
# MATCH pattern key的正则模式
# count 遍历的limit hint; limit 1000 不是限定返回结果集数量而是限定服务器单次遍历的字段槽位数量
第一次遍历时, cursor为0,将返回结果集的第一个整数作为下一次遍历的cursor, 一直遍历到返回的cursor 值为0时结束
scan 0 match key100* count 1000
1) 13976
2) 1) "key100xxx1"
2) "key100xxx2"
scan 13976 match key100* count 1000
1) 2000
2) 1) "key100xxx11"
2) "key100xxx22"
....
scan 2000 match key100* count 1000
1) 0
2) 1) "key100xxx111"
2) "key100xxx222"
Redis中的key 都存储在一个字典中, 字典结构(类似Java中 HashMap)
字典= 一维数组 + 二维链表结构
一维数组大小是 2 ^n , 扩容一次是 2^ (n+1)
Scan 返回的游标就是第一位数组的位置索引, 这个位置索引称为 slot, limit 参数就表示需要遍历的槽位数
- 点赞
- 收藏
- 关注作者
评论(0)