有序集合类型
有序集合是在集合的基础上,对集合中的每一个元素都关联了一个分数,这使得我们不仅可以完成插入、删除和判断元素是否存在等集合类型支持的操作,还可以获得分数最高或者最低的前N个元素,获得指定分数范围内的元素等与分数有关的操作。
虽然集合中的每个元素是不同的,但是他们的分数是可以相同的。
有序集合类型在某些方面和列表类型有些类似:
- 二者都是有序的。
- 二者都可以获得某一范围的元素。
但是二者有着很大的区别,这使得它们的应用场景也是不同的: - 列表类型是通过链表实现的,获取靠近两端的数据速度极快,而当元素增多后,访问中间数据的速度回比较慢,所以它更加适合实现对中间元素访问较少的应用。
- 有序集合类型获取位于中间部分的数据速度是很快的O(1)。
- 列表不能简单地调整某个元素的位置,但是有序集合可以(通过更改这个元素的分数)。
- 有序集合要比列表类型更耗费内存。
ZADD key score member
ZADD命令用来向有序集合中加入一个元素和该元素的分数,如果元素已经存在则会替换原来的分数。该命令返回值是加入集合中的元素的个数。ZADD命令示例如下:
127.0.0.1:6379> ZADD sunshine 150 yang 90 xiang 60 zi
(integer) 3
127.0.0.1:6379> ZADD sunshine 0 zi
(integer) 0
分数不仅可以是整数,还支持双精度浮点数。
127.0.0.1:6379> ZADD sunshine 0.1 zi
(integer) 0
SCORE key member用来获取元素的分数,命令示例如下:
127.0.0.1:6379> ZSCORE sunshine yang
"150"
ZRANGE\ZREVRANGE key start stop [WITHSCORES]用来获得排名在某个范围的元素列表,ZRANGE会是以从小到大的顺序返回索引从start到stop之间的所有元素(包含两端的元素)。
ZRANGE的索引也是从0开始。负数代表从后向前找(-1表示最后一个元素),示例如下:
127.0.0.1:6379> ZRANGE sunshine 0 1
1) "zi"
2) "xiang"
127.0.0.1:6379> ZRANGE sunshine 0 -1
1) "zi"
2) "xiang"
3) "yang"
如果想在获取元素的同时加上元素的分数,就可以在命令后加上WITHSCORES参数,命令示例如下:
127.0.0.1:6379> ZRANGE sunshine 0 -1 WITHSCORES
1) "zi"
2) "0.10000000000000001"
3) "xiang"
4) "90"
5) "yang"
6) "150"
ZRANGE命令的时间复杂度是O(logn+m)(其中n为有序集合的基数,m为返回的元素个数)。
如果两个元素的分数相同,Redis会按照字典顺序(“0″<“9″<“A”<“Z”<“a”<“z”这样的顺序)来进行排列,如果元素是中文的话,取决于中文的编码方式,示例如下:
127.0.0.1:6379> ZRANGE sunshine 0 -1 WITHSCORES
1) "\xe6\x9d\xa8"
2) "1"
3) "\xe6\x9d\xbe"
4) "2"
5) "\xe9\x9c\x96"
6) "3"
ZREVRANGE命令和ZRANGE命令唯一不同在于ZREVRANGE命令是按照元素分数从大到小的顺序给出的结果。
ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]命令是按照元素从小到大的顺序返回分数在min和max之间(包含min和max)的元素,示例如下:
127.0.0.1:6379> ZRANGEBYSCORE sunshine 1 10
1) "y"
2) "a"
3) "s"
4) "o"
5) "g"
6) "l"
7) "i"
如果希望分数范围不包含端点值,可以在分数前加上”(“符号。例如,希望返回1到8的分数,可以包含1分,但是不包含10分,示例如下:
127.0.0.1:6379> ZRANGEBYSCORE sunshine 1 (10
1) "y"
2) "a"
3) "s"
4) "o"
5) "g"
6) "l"
结合上面例子可以看出有什么区别。
min和max还支持无穷大,同ZADD命令一样,-inf和+inf分别表示负无穷和正无穷(比如你希望得到所有分数高于1分的集合,但你却不知道最高分是多少)。是示例如下:
127.0.0.1:6379> ZRANGEBYSCORE sunshine 1 +inf
1) "y"
2) "a"
3) "s"
4) "o"
5) "g"
6) "l"
7) "i"
8) "n"
WITHSCORES参数的用法和ZRANGE命令一样,不在赘述。
[LIMIT off set]参数和SQL中的用法基本相同。即在获得的元素列表的基础上向后偏移offset个元素,并且只获取前count个元素,示例如下:
# 列出分数高于2分的从第二个人开始的三个人
127.0.0.1:6379> ZRANGEBYSCORE sunshine 2 +inf LIMIT 1 3
1) "s"
2) "o"
3) "g"
如果想获得分数低于或等于10的前三个人,可以使用ZREVRANGEBYSCORE命令实现。该命令的用法和ZREVRANGE的命令相似(注意min和max参数的顺序和ZRANGEBYSCORE命令是相反的),示例如下:
127.0.0.1:6379> ZREVRANGEBYSCORE sunshine 10 -inf LIMIT 1 3
1) "l"
2) "g"
3) "o"
结果也是倒着给出的。
ZINCRBY key increment member命令可以增加一个元素的分数,返回值是更改后的分数。例如,想给y加100分,示例如下:
127.0.0.1:6379> ZINCRBY sunshine 100 y
"101"
increment也可以是个负数表示减分,例如,给y减100分,示例如下:
127.0.0.1:6379> ZINCRBY sunshine -100 y
"1"
但是redis里没有ZDECRBY命令,故只能通过变更increment的正负来完成加和减。
ZCOUNT key min max命令用于获得指定分数范围内的元素个数,示例如下:
127.0.0.1:6379> ZCOUNT sunshine 1 10
(integer) 7
ZREM key member [member…]用于删除一个或多个元素,返回值是成功删除元素的个数(不包含本来就不存在的元素)。
127.0.0.1:6379> ZREM sunshine y a s
(integer) 3
ZREMRANGEBYRANK key start stop命令用来按照元素分数从小到大的顺序删除处在指定排名范围内的所有元素,并返回删除的元素数量。
127.0.0.1:6379> ZREMRANGEBYRANK sunshine 0 5
(integer) 5
ZREMRANGEBYSCORE key min max命令会删除指定分数范围内的所有元素,参数min和max特性和ZRANGEBYSCORE命令中的一样,返回值是删除的元素数量,示例如下:
127.0.0.1:6379> ZREMRANGEBYSCORE sunshine (0 3
(integer) 3
127.0.0.1:6379> ZRANGE sunshine 0 -1
1) "sundial"
2) "dreams"
ZRANK命令会按照元素分数从小到大的顺序获得指定的元素的排名(从0开始,即分数最小的元素排名为0),示例如下:
127.0.0.1:6379> ZRANK sunshine yang
(integer) 0
ZREVRANK命令则是ZRANK的反排序(分数最大的元素排名为0)。
计算有序集合的交集
ZINTERSTORE destiniation numberkeys key [key…] [WEIGHTS weight [weight…] [AGGREGATE SUM|MIN|MAX]],ZINTERSTORE命令用来计算多个有序集合的交集并将结果存储在destination键中(同样以有序集合类型存储),返回值为destination键中的元素个数。
当AGGREGATE是SUM时(也就是默认值),destination键中的元素的分数是每个参与计算的集合中该元素分数的和,示例如下:
127.0.0.1:6379> ZADD A 1 1 2 2 3 3
(integer) 3
127.0.0.1:6379> ZADD B 3 3 4 4 5 5
(integer) 3
127.0.0.1:6379> ZADD C 3 3 5 5 6 6
(integer) 3
127.0.0.1:6379> ZINTERSTORE interset 3 A B C
(integer) 1
127.0.0.1:6379> ZRANGE interset 0 -1 WITHSCORES
1) "3"
2) "9"
当AGGREGATE是MIN时,destination键中元素的分数是每个参与计算的集合中该元素分数的最小值,示例如下:
127.0.0.1:6379> ZINTERSTORE interset 3 A B C AGGREGATE MIN
(integer) 1
127.0.0.1:6379> ZRANGE interset 0 -1 WITHSCORES
1) "3"
2) "3"
当AGGREGATE是MAX时,destination键中元素的分数是每个参与计算的集合中该元素分数的最大值,示例如下:
127.0.0.1:6379> ZINTERSTORE interset 3 A B C AGGREGATE MAX
(integer) 1
127.0.0.1:6379> ZRANGE interset 0 -1 WITHSCORES
1) "3"
2) "3"
ZINTERSTORE命令还能够通过WEIGHTS参数设置每个集合的权重,每个集合在参与计算时元素的分数会被乘上该集合的权重,示例如下:
127.0.0.1:6379> ZINTERSTORE interset 3 A B C WEIGHTS 1 2 3
(integer) 1
127.0.0.1:6379> ZRANGE interset 0 -1 WITHSCORES
1) "3"
2) "18"
另外还有一个命令与ZINTERSTORE命令用法一样,名为ZUNIONSTORE,它的作用是计算集合之间的并集,这里不再赘述,示例如下:
127.0.0.1:6379> ZRANGE interset 0 -1 WITHSCORES
1) "1"
2) "1"
3) "2"
4) "2"
5) "4"
6) "4"
7) "6"
8) "6"
9) "3"
10) "9"
11) "5"
12) "10"
127.0.0.1:6379> ZRANGE interset 0 -1 WITHSCORES
1) "1"
2) "1"
3) "2"
4) "2"
5) "3"
6) "3"
7) "4"
8) "4"
9) "5"
10) "5"
11) "6"
12) "6"
127.0.0.1:6379> ZUNIONSTORE interset 3 A B C WEIGHTS 1 2 3
(integer) 6
127.0.0.1:6379> ZRANGE interset 0 -1 WITHSCORES
1) "1"
2) "1"
3) "2"
4) "2"
5) "4"
6) "8"
7) "3"
8) "18"
9) "6"
10) "18"
11) "5"
12) "25"
但是有没有ZDIFF命令呢?
127.0.0.1:6379> ZDIFF interset 3 A B C WEIGHTS 1 2 3
(error) ERR unknown command 'ZDIFF'
答案是没有ZDFIFF命令,我的理解是,DIFF命令是差集运算,即差集后的集合的分数不会改变(权重例外),故没有声明这个命令。