set类型
类型介绍
与list不同的主要两点:
1.集合的元素是唯一的(不能重复)
2.集合的元素是无序的。
和list相同的是,集合的每个元素也都是string类型,如果要存储结构化数据,可以用json存储。
sadd / smembers / sismember
sadd
将⼀个或者多个元素添加到 set 中。注意,重复的元素⽆法添加到 set 中。所以是随机删除。
SADD key member [member ...]
这里的元素用member来称呼,这样做也是方便区别不同类型的元素,比如hash中就是field。
smembers
SMEMBERS key
sismember
SISMEMBER key member
scard
scard
获取⼀个 set 的基数(cardinality),即 set 中的元素个数。
SCARD key
spop / srandmember
spop
pop一般表示从末尾删除某一元素,但是集合中的元素是无序的,哪有什么 “末尾”?
SPOP key [count]
srandmember
与spop不同的是,srandmember不会删除,也是完全随机返回一个元素。
srandmember key [count]
smove / srem
smove
SMOVE source destination member
这个 source 表示要从哪个key中删除元素,destination 表示要将这个删除的元素插入到哪个key中。
需要注意:是先从 source 删除member,然后再将member插入到destination 中。
如果要移动的元素destination 中已经存在,那么也不会报错,因为集合中元素是唯一的,所以destination 中还是只有一份,并且source 中照样还是会删除member。
srem
rem就是remove的缩写
SREM key member [member ...]
集合的一些基本概念
以下就介绍一些Redis 交集,并集,差集的操作
sinter / sinterstore
sinter
获取给定 set 的交集中的元素。
SINTER key [key ...]
sinterstore
获取给定 set 的交集中的元素并保存到⽬标 set 中。
SINTERSTORE destination key [key ...]
sunion / sunionstore
sunion
获取给定 set 的并集中的元素。
SUNION key [key ...]
sunionstore
SUNIONSTORE destination key [key ...]
sdiff / sdiffstore
sdiff
获取给定 set 的差集中的元素。
SDIFF key [key ...]
之前求交集和并集,交换两个集合的顺序,结果是一样的,但是求差集这里,交换集合的顺序,求出来的结果是不一样的。
比如集合A的元素1 2 3 4 集合B的元素 3 4 5 6 ,那么求集合A与集合B的差集结果就是 1 2,反过来求集合B与集合A的差集就是5 6。
sdiffstore
获取给定 set 的差集中的元素并保存到⽬标 set 中。
SDIFFSTORE destination key [key ...]
内部编码
1)当元素个数较少并且都为整数时,内部编码为 intset:
127.0.0.1:6379> sadd setkey 1 2 3 4
(integer) 4
127.0.0.1:6379> object encoding setkey
2)当元素个数超过 512 个,内部编码为 hashtable:
127.0.0.1:6379> sadd setkey 1 2 3 4
(integer) 513
127.0.0.1:6379> object encoding setkey
"hashtable"
3)当存在元素不是整数时,内部编码为 hashtable:
127.0.0.1:6379> sadd setkey a
(integer) 1
127.0.0.1:6379> object encoding setkey
"hashtable"
set应用场景
实现标签
比如用set保存用户的标签 。
用户画像:分析用户的一些特征,然后进行投其所好。
比如喜欢浏览化妆品 或者裙子这些商品 的用户可以打上女人的标签,喜欢浏览数码产品的可以打上男人的标签等等。
或者还可以基于这些标签,计算两个用户是否有相同的爱好。
计算共同好友
其实跟标签的效果差不多,我们使用qq的时候就经常看到qq会给我们推荐一些用户,说这些用户有多少共同好友。其实也是通过求交集的方式来计算的。
计算UV (用户量)
补充概念:
PV:page view,用户每次访问服务器,就会产生一次PV,多次访问就产生多次。
UV:user view:每一个用户访问服务器,只会产生一次UV,多次访问也只有一次。
用UV的信息就可以用set进行去重,然后求出set的大小就可以得知这个服务器的用户量了。
如果想统计访问量就更简单了,每次访问都会产生一个日志,然后记录这个日志的数量就可以了
一般可以每日将这些数据刷新一次,这样就可以动态了解这个网站的访问量和用户量了。
zset类型
类型介绍
注意:在有序集合这里,有序指的是升序,而不是顺序,这里的概念与集合那里是不一样的。
简单对比以下list set zset
zadd / zrange
ZADD key [NX | XX] [GT | LT] [CH] [INCR] score member [score member...]
另外 在Redis5版本中,GT LT 选项是没有的
LT:less than ,只有修改的key的分数 小于原来的,才会修改成功。
GT:greater than,只有当修改的key的分数大于原来的,才会修改成功。
注意:不要把这里的member score当作键值对来看待,在键值对中,是有明确的 角色区分 的,只能通过 键 -> 值 的方式,而不能反过来。但是对于有序集合来说,既可以通过member找到对应的score,也可以通过score匹配对应的member。
zrange
区间查找有序集合中的元素,用法跟lrange类似。
zrange key start stop [withscores]
下标依旧可以为负数,意义跟以往的一样。
withscores:选择是否要把分数一起显示出来。
zcard / zcount
zcard
获取⼀个 zset 的基数(cardinality),即 zset 中的元素个数。
ZCARD key
zcount
返回分数在 min 和 max 之间的元素个数,默认情况下,min 和 max 都是包含的,可以通过 ( 排除。
ZCOUNT key min max
zset的底层实现其实是使用跳表的,一般来说查找满足这个区间的元素,那么时间复杂度不应该是O(N)吗?其实Redis给每个元素都加上了一个 “排行” 也就是它是第几个元素,所以就只需要找到min和max就可以了。
另外,这里的查找都是按照闭区间来查找的,[min,max],如果想要以开区间的方式,那么需要在边界值前加上 ( 。比如
注意这里加括号的方式是不大符合直觉的。
zrevrange / zrangebyscore
zrevrange
备注:这个命令可能在 6.2.0 之后废弃,并且功能合并到 ZRANGE 中。
ZREVRANGE key start stop [WITHSCORES]
这里的start 和stop的顺序和 zrange是一样,只是zrevrange会把结果逆序返回。
zrangebyscore
ZRANGEBYSCORE key min max [WITHSCORES]
zpopmax
删除并返回分数最⾼的 count 个元素。
ZPOPMAX key [count]
count表示删多少个,默认一个。
如果删除的元素分数是一样的,那么会按照字典序来删。
另外关于这个时间复杂度这里,我们知道zset是跳表结构的,所以元素都是有序的,又因为删除的元素是最大值,所以为什么不搞一个值来记录尾部,这样删除的时候,查找的时间复杂度不就降为O(1)了吗?确实,但是Redis目前并没有这么做 。
bzpopmax
bzpopmax
ZPOPMAX 的阻塞版本。
BZPOPMAX key [key ...] timeout
timeout就是要等待多少时间,也就是超时时间,单位是 秒,支持小数,比如 0.1s表示 100ms。
这里的timeout是不能省略的。另外要注意这里的时间复杂度, 虽然bzpopmax可以等待多个key,但是它只会删除最先就绪的那一个key,并且只删除一个。
zpopmin / bzpopmin
zpopmin
删除并返回分数最低的 count 个元素。
ZPOPMIN key [count]
用法与zpopmax一致。
bzpopmin
ZPOPMIN 的阻塞版本。
BZPOPMIN key [key ...] timeout
zrank / zrevrank / zscore
zrank
返回指定元素的排名,升序。
这里的排名其实就是下标,从0开始的。
ZRANK key member
zrevrank
返回指定元素的排名,降序。
ZREVRANK key member
zscore
返回指定元素的分数。
ZSCORE key member
这里zscore的时间复杂度按我们之前的想法应该是O(logN)才对,但是注意这里是O(1),这是因为Redis觉得这个命令应该是高频使用的命令,容易成为性能瓶颈,所以用额外的空间进行了特殊的优化,使其时间复杂度降到了O(1)。
zrem / zremrangebyrank / zremrangebyscore
zrem
删除指定的元素。
ZREM key member [member ...]
zremrangebyrank
按照排序,升序删除指定范围的元素,左闭右闭。 这里不能通过加(的方式表示开区间
ZREMRANGEBYRANK key start stop
注意这里的时间复杂度那里是 N + M,而不是 N * M,因为这里的查找只需要查找一次。
zremrangebyscore
按照分数删除指定范围的元素,左闭右闭。 同理
ZREMRANGEBYSCORE key min max
zincrby
zincrby
为指定的元素的关联分数添加指定的分数值。(这个增量可以是浮点数,也可以是负数)
ZINCRBY key increment member
zinterstore
zinterstore
ZINTERSTORE destination numkeys key [key ...] [WEIGHTS weight[weight ...]] [AGGREGATE <SUM | MIN | MAX>]
关于这里的时间复杂度,看似很复杂,其实不用特别记忆
关于命令部分:
destination ,我们将求完交集的结果会放入到这个key中。
numkeys 参数表示我们要传入多少个key,如果要传入两个key,那么就填入2
[WEIGHTS weight [weight ...]] 这里表示权重,比如我们之前传入了两个key,key1 key2 ,我们可以按照一定的比重来计算分数,比如key1的元素分数只想占40%,key2的想占60%,那么这里就可以传入 WEIGHTS 0.4 0.6 。当然也可以传入大于1的数,不一定表示比重。
[AGGREGATE <SUM | MIN | MAX>] 这里表示对交集元素的分数怎么处理,sum表示将交集的元素相加,min表示取最小值,max取最大值。
使用示例:
可以看见,求交集默认是将元素的分数相加。然后我们还给了权重,将key1的元素的分数 * 2,将key2的元素的分数 * 3。
zunionstore
ZUNIONSTORE destination numkeys key [key ...] [WEIGHTS weight[weight ...]] [AGGREGATE <SUM | MIN | MAX>]
这里的用法和细节跟zinterstore是一样的。
命令小结:
zset的内部编码
另外跳表还比较适合范围查询。
zset应用场景
排行榜系统
复杂一点的,比如微博的热度排行榜。