Redis手法:有序集(SortedSet)的操作转载
原创有序集合(Sorted Set)是Redis保存需要排序的数据的重要数据结构。例如,排行榜、班级的中文分数、公司员工的工资、论坛的帖子等。在有序的集合中,每个元素都有一个score(权重)对元素进行排序。它有三个要素:key、member和score。以中国的成就为例,key是考试的名称(期中考试、期末考试等),member这是学生的名字,score是成绩。
有序集合有两个基本用途:排序和聚合,如以下几个示例所示。
排序
假设老师需要处理期中考试的语文成绩,他做的第一件事就是把学生的成绩录入系统。
Li Lei成绩70分
127.0.0.1:6379> ZADD mid_test 70 "Li Lei"
(integer) 1
Han Meimei成绩70分
127.0.0.1:6379> ZADD mid_test 70 "Han Meimei"
(integer) 1
tom成绩99.5分
127.0.0.1:6379> ZADD mid_test 99.5 "Tom"
(integer) 1
排行榜
有序的收藏自然是排名的利器。带上就行了score的member将它插入有序的集合中,您可以按正向或反向顺序取出数据。这需要ZREVRANGE(逆序)和ZRANGE(正序)。
得分排行榜
127.0.0.1:6379> ZREVRANGE mid_test 0 -1 WITHSCORES
1) "Tom"
2) "99.5"
3) "Li Lei"
4) "70"
5) "Han Meimei"
6) "70"
分段统计
有序集合还支持按score查询间隔:ZREVRANGEBYSCORE对于倒排查询,ZRANGEBYSCORE是一个正序列。例如,要知道90不止一个学霸:
127.0.0.1:6379> ZREVRANGEBYSCORE mid_test 100 90 WITHSCORES
1) "Tom"
2) "99.5"
聚合
有序集合本质上是集合,当然会有交集( ZINTERSTORE )和并集( ZUNIONSTORE )运算。
相交和串联
交集
ZINTERSTORE 取所有集合的交集。有两套A和B例如,要在交叉口C,这就是逻辑:
- A和B中共有的member,将添加到C中,其score等于A、B中score之和。
- 不同时在A和B的member,将不会添加C中。
一个班又进行了一次期末考试,同时来了一位新同学。Jerry。
127.0.0.1:6379> ZADD fin_test 88 "Li Lei"
(integer) 1
127.0.0.1:6379> ZADD fin_test 75 "Han Meimei"
(integer) 1
127.0.0.1:6379> ZADD fin_test 99.5 "Tom"
(integer) 1
127.0.0.1:6379> ZADD fin_test 100 "Jerry"
(integer) 1
老师应该按照中考和期末考试的总成绩按期安排座位,这是对的。mid_test和fin_test过了一个十字路口。
127.0.0.1:6379> ZINTERSTORE sum_point 2 mid_test fin_test
(integer) 3
127.0.0.1:6379> ZREVRANGE sum_point 0 -1 WITHSCORES
1) "Tom"
2) "199"
3) "Li Lei"
4) "158"
5) "Han Meimei"
6) "145"
结果显示了学生的总成绩。
但结果中没有新来的人。Jerry学生(虽然TA考了100点)。这是一号坑。
并集
ZUNIONSTORE 计算所有集合的并集。有两套A和B例如,获取和设置C,这就是逻辑:
- A的所有member会加到C中,其score与A中相等
- B的所有member会加到C中,其score与B中相等
- A和B中共有的member,其score等于A、B中score之和。
假设某公司要核算工资总支出,先按各部门单独核算,再按财政统一待遇。
程序员的工资
127.0.0.1:6379> zadd programmer 2000 peter
(integer) 1
127.0.0.1:6379> zadd programmer 3500 jack
(integer) 1
127.0.0.1:6379> zadd programmer 5000 tom
(integer) 1
经理工资
127.0.0.1:6379> zadd manager 2000 herry
(integer) 1
127.0.0.1:6379> zadd manager 3500 mary
(integer) 1
127.0.0.1:6379> zadd manager 4000 tom
(integer) 1
财政统一待遇。
127.0.0.1:6379> zunionstore salary 2 programmer manager
(integer) 5
127.0.0.1:6379> zrange salary 0 -1 withscores
1) "herry"
2) "2000"
3) "peter"
4) "2000"
5) "jack"
6) "3500"
7) "mary"
8) "3500"
9) "tom"
10) "9000"
结果显示,工资总支出。
但结果中的程序员tom和经理tom是两个人,但工资算在一起。这里是2号坑。
避免踩坑
你还记得上面提到的1号坑和2号坑吗?
坑一:
当进行ZINTERSTORE在操作过程中,如果聚合操作的源集合中的元素不同,则聚合结果集只是交集。如果在汇总后发现某些要素丢失,请检查来源收集要素是否相同。
坑二:
当进行ZUNIONSTORE操作,如果在其中执行聚合操作的源集存在相同的元素,则聚合结果集是相同的元素。score等于来源收集要素。score总和。如果发现一些元素经过聚合后。score异常,请检查源集合是否有相同的元素。
我踩到的那个坑:
做用户的feed(timeline),我需要用我自己发布的信息来汇总我关心的人。
timeline & feed
应该用ZUNIONSTORE将所有信息聚合在一起。
后来,用户反馈说timeline排序有误,自己发布的信息总是排在最前面。由于早期的原因,后来确定了原因bug,自己可以关注自己,导致关注和自己的反复聚合。踩2号坑。
为什么要踩在坑上?
以2号坑为例,为什么有相同的元素,score它会成为原始元素的总和吗?
因为ZINTERSTORE和ZUNIONSTORE有一些参数是AGGREGATE,表示结果集的聚合,这是可取的。SUM、MIN、MAX他们中的一个。缺省值为SUM。
因此,如果不指定聚合方法,则缺省值为。SUM,也就是总和。
默认情况下使用的参数 SUM ,则可以将成员放入所有集合中。 score 值之 和 作为结果集的成员。 score 值;使用参数 MIN ,则可以将成员放入所有集合中。 最小 score 值作为结果集的成员。 score 值;以及参数 MAX 是将所有成员放入集合中。 最大 score 值作为结果集的成员。 score 值。
文件如上所述。
有序集的总和
使用场景:排行榜、有序列表、聚合;
算法复杂性:
- 增删:O(M*log(N)), N 是有序集合的基数, M 成功操作(添加、删除)的成员数。
- 查询:O(log(N)+M), N 是有序集合的基数,而 M 是结果集的基数。
- 聚合:O(N)+O(M log(M)), N 是给定有序集的基数之和, M 是结果集的基数。
- 总数:O(1)
注意事项:
- ZINTERSTORE操作时,如果在汇总后发现某些要素丢失,请检查来源收集要素是否相同。
- ZUNIONSTORE操作时,如果发现一些元素经过聚合后。score异常,请检查源集合是否有相同的元素。
博客链接: http://spetacular.github.io/2015/11/01/redis-zunionstore-tip.htm
作者:兔子David
链接:https://www.jianshu.com/p/6c4b7d697f45
资料来源:简讯
版权归作者所有。请联系作者以获得商业转载的授权,并注明非商业转载的来源。
版权声明
所有资源都来源于爬虫采集,如有侵权请联系我们,我们将立即删除
itfan123




