Redis 数据类型及应用场景——zset
今天再来聊下redis中的有序集合sort set,也是redis里面最高级别的一种数据类型,之前有聊到集合(set) ,从名字上来看这两种类型很相似,不过两者可能在设计之初就被赋予了不同的使命吧。顾名思义,有序集合就是集合中的元素是按照一定的顺序排列的,是有序的,我们之前说的那个集合是无序的,那接下来我们来详细的看看redis 有序集合。
1.简介
有序集合(sort set)是在集合类型的基础上为每个元素关联一个分数,那这使得我们不仅可以完成插入,删除和判断元素是否存在等集合操作,还能够获得分数最高(或最低)的前N个元素,获得指定分数范围内的元素等分数有关的操作。虽然集合中每个元素都是不相同的,但是分数可以相同;内部使用hashMap或者跳跃表来实现的。下面给大家画一张图:

2. 命令
这里关于有序集合的命令写得比较全,小伙伴们可以有选择的看
- 增加元素/删除元素/获取元素的分数/获取集合中的元素数量
zadd key score member [score member ...] score(分数),如果分数是小数的话可能会存在精度的问题,本人觉得比较省事的做法就是把数据转换为整数后再进行存放,比如乘以100。
zrem key member [member] ,返回成功删除的元素数量
zscore key member 返回指定元素的分数
redis> zadd scoreboard 100 xiaoming 89 Tom 95 Peter // 往集合中加入三条记录
(integer) 3
redis> zrem scoreboard xiaoming
(integer) 1
redis > zscore scoreboard Peter
"95"
redis > zcard scoreboard
(integer) 4
如果scoreboard 这个集合中已经存在Tom这位同学,那么执行zadd 后会覆盖原来的。
- 获得排名在某个范围内的元素列表/按照排名范围删除元素
zrange key start stop [withscores]
zrevrange key start stop [withscores]
zremrangebyrank key start stop 按照元素分数从小到大的顺序删除处在指定排名范围内的所有元素,并返回删除的元素数量,注意是包含start和stop位置上的元素
redis > zrange scoreboard 0 -1 //获取集合中的所有元素,并且按照分数来从小到大排好序了。
1) "Tom"
2) "Peter"
3) "xiaoming"
redis >zrange scoreboard 0 -1 withscores // 加上withscores参数 额外再返回分数。
1) "Tom"
2) "89"
3) "Peter"
4) "95"
5) "xiaoming"
6) "100"
redis > zrevrange scoreboard 0 -1 //按照分数从高到低排序。
1) "xiaoming"
2) "Peter"
3) "Tom"
redis > zadd testRem 1 a 2 b 3 c 4 d
(integer) 4
redis > zremrangebyrank testRem 0 2
(integer) 3
这里就展示这三个操作,不过需要注意的是start和end 这两个值的选取,集合中的数据下标是从0开始的,所以第一个元素标号是0,第二个元素标号是1,zrange和zrevrange命令中的start和end返回的元素是包含start和end标号下的元素的。所以我们如果只想返回第一个元素可以这样写
redis> zrange scoreboard 0 0
1) "Tom"
还有一个需要注意的就是如果两个元素的分数相同,Redis会按照字典顺序(也就是 "0" < "9"<"A"<"Z"<"a"<"z" 这样的顺手来排列)来进行排序,但如果是中文则取决于中文的编码方式,具体的没做深入的研究,这里就不具体说了。
不过貌似没有只获取所有元素分数的命令
- 获取指定分数范围内的元素/获取指定分数范围内的元素个数/按照分数范围删除元素
zrangebyscore key min max [WITHSCORES] [LIMIT offset count] //从小到大
zrevrangebyscore key min max [WITHSCORES] [LIMIT offset count] //从大到小
zcount key min max
zremrangebyscore key min max
redis > zrangebyscore scoreboard 80 100 WITHSCORES
1) "Tom"
2) "89"
3) "Peter"
4) "95"
5) "xiaoming"
6) "100"
redis > zcount scoreboard 80 90
(integer) 1
注意:包含min和max。 如果希望不要返回端点的数据可以使用"(" 符号。例如:
redis > zrangebyscore scoreboard 80 (100 WITHSCORES
1) "Tom"
2) "89"
3) "Peter"
4) "95"
redis >
如果需要返回大于某个值或者小于某个值的所有元素可以使用+inf和-inf ,分别表示正无穷大和负无穷大,比如希望返回高于89分,但是不包含89分的所有元素,但是你不知道最高分是多少,这个时候就可以这样写:
redis> zrangebyscore scoreboard (89 +inf WITHSCORES
1) "Peter"
2) "95"
3) "xiaoming"
4) "100"
zrangebyscore 这个命令还向我们提供了limit offset count ,这个类型于我们经常使用的sq语句里面的limit offset count,就是从offset这个文位置开始获取count个元素
- 获得元素的排名
zrank key member
zrevrank key member
redis > zrank scoreboard Peter
(integer) 2
redis > zrevrank scoreboard Peter
(integer) 1
注意:排名是从0开始计数的。
- 增加或者减少某个元素的分数
zincrby key increment member ,其中increment既可以为正数(表示增加)也可以为负数(表示减少),这个increment就好为整数,不然也是会出现精度问题,导致数据不太对。
redis > zincrby scoreboard -1 Peter
"94"
6)计算有序集合的交集
ZINTERSTORE destination numkeys key [key ...] ,该命令用来计算多个有序集合的交集并将结果存放在destination键中(同样以有序集合类型存储),返回值为destination键中的元素个数。destination键中的元素分数是有aggregate参数决定的。
(1) 当aggregate是sum时(也就是默认值),destination 键中的元素的分数是每个参与计算的集合中的改元素分数的和。
redis > zadd sortedSets3 1 a 2 b
(integer) 2
redis > zadd sortedSets4 2 b 3 c
(integer) 2
redis > ZINTERSTORE sortedSetsResult1 2 sortedSets3 sortedSets4
(integer) 1
redis > zrange sortedSetsResult1 0 -1 WITHSCORES
1) "b"
2) "4"
(2) 当aggregate是min或者max时,destination键中的元素的分数是每个参与计算的集合中改元素分数的最小值或者最小值。
redis > ZINTERSTORE sortedSetsResult2 2 sortedSets3 sortedSets4 AGGREGATE min
(integer) 1
redis > zrange sortedSetsResult2 0 -1 WITHSCORES
1) "b"
2) "2"
ZINTERSTORE 命令还能够通过设置WEIGHTS参数设置每个集合的权重,每个集合在参与计算时元素的分数会被乘上改集合的权重。
redis > ZINTERSTORE sortedSetsResult3 2 sortedSets3 sortedSets4 WEIGHTS 1 10
(integer) 1
redis > zrange sortedSetsResult3 0 -1 WITHSCORES
1) "b"
2) "22"
另外还有一个与ZINTERSTORE命令的用法相同的命令-zunionstore,它的作用是计算集合间的并集,这里就不具体展示了。下面我们看下有序集合的相关应用场景
3. 实践
1. 实现按点击量排序
以文章的ID作为键,文章的点击量作为分数,如果将键名命名为posts:page.view,每次访问某篇文章的时候就zincrby posts:page.view 1 文章ID 来更新点击量,需要按照点击量来显示文章的时候 zrevrange posts:page.view start end 来获取文章的某一范围内的文章id。
2. 按照文章的发布时间来排序
如果文章的发布时间是可以修改的,我们可以使用有序集合来实现,元素是文章的id,元素的分数是发布时间,通过修改元素的对应的分数就可以达到更改时间的目的。
3. 排行榜
比如要取得某个班级的前十名的学生名单,使用 zadd scoreboard <score> <username>将学生的分数保存,那么取得前十名的同学名单是很容易的,zrevrange scoreboard 0 9
4. 可以做带权重的队列,比如普通消息的score为1,重要消息的score为2,然后工作线程可以选择score的倒序来获取工作任务。让重要的任务优先执行。
4. 有序集合和集合的区别
首先他们都是集合,都支持添加,删除,获取元素。但是它们应用的场景是不一样的,
- Set(集合) 这种类型对于不同集间求交集,并集,差集是很方便的,因此它非常适合用在一些社交的场景,比如某两个人共同关注的人。
- Zset(有序集合),它更多的应用于一些需要按照某种方式排序的情况下,操作是很方便的,例如:求集合中分数最高的几个人的名单,使用zrevrange key 0 5
5. 有序集合和列表的区别
我们先来看下相同的地方:
- 两者都是有序的,注意list的顺序是按照插入的顺序,而有序集合是按照分数来排序的,不是添加时候的顺序。
- 两者都可以获得某一范围内的元素,说的具体一点,就是list获取某一范围内的元素是根据元素的位置来的,有序集合可以获取排名在某一范围内的元素,也可以获取分数在某一范围内的元素。
下面来说下二者的区别 - list取链表的中间元素是相对没有取两端的数据块,但是有序集合使用散列表或者跳跃表来作为底层实现,即使取中间的元素的速度也是很快的。
- 列表是不能简单的调整某个元素的位置的,但是有序集合可以,改变元素的分数即可。我们知道list虽说是可以存储各种列表,但是如果需要排序的话,就可能比较麻烦
- 有序集合要比列表类型更消耗内存。
关于有序集合就先写到这吧,如果有小伙伴需要补充的,欢迎在下面和我留言哦,看到会及时回复的。