Redis(七) -- Redis数据类型(四) -- sorted set(zset)

1. 简介

Redis有序集合和普通集合set很相似,是一个没有重复元素的字符串集合。

不同之处时有序集合的每个成员都关联了一个评分(score),这个评分被用来按照最低分到最高分的方式排序集合种的成员。集合的成员是唯一的,但是分数可以重复

因为元素是有序的,所有可以很快的根据评分或者次序来获取一个范围的元素。

访问有序集合的中间元素也是非常快的,因为你能够使用有序集合作为一个没有重复成员的智能列表。

最经典的应用就是排行榜。

2. 数据结构

SortedSet是Redis提供的一个非常特别的数据结构,一方面它等价于java的数据结构Map<String, double>,可以给每一个元素value赋予一个权重score,另一方面它又类似于TreeSet,内部的元素会按照权重进行排序,可以得到每个元素的名次,还可以通过score的范围来获取元素的列表。

SortedSet的底层使用了两个数据结构:

  1. hash;hash的作用就是关联元素value和权重score,保障元素value的唯一性,可以通过元素value找到score
  2. 跳跃表,跳跃表的目的在于给元素value排序,根据score的范围获取元素列表

1.zadd:添加元素到有序集合中

  • 格式ZADD key [NX|XX] [CH] [INCR] score member [score member ...]
  • 添加时可以指定多个分数/成员(score/member)对。 如果指定添加的成员已经是有序集合里面的成员,则会更新改成员的分数(scrore)并更新到正确的排序位置。
  • 如果key不存在,将会创建一个新的有序集合(sorted set)并将分数/成员(score/member)对添加到有序集合,就像原来存在一个空的有序集合一样。如果key存在,但是类型不是有序集合,将会返回一个错误应答。
  • 分数值是一个双精度的浮点型数字字符串。+inf和-inf都是有效值。
  • 参数列表
    • XX: 仅仅更新存在的成员,不添加新成员。
    • NX: 不更新存在的成员。只添加新成员。
    • CH: 修改返回值为发生变化的成员总数,原始是返回新添加成员的总数 (CH 是 changed 的意思)。更改的元素是新添加的成员,已经存在的成员更新分数。 所以在命令中指定的成员有相同的分数将不被计算在内。注:在通常情况下,ZADD返回值只计算新添加成员的数量。
    • INCR: 当ZADD指定这个选项时,成员的操作就等同ZINCRBY命令,对成员的分数进行递增操作
  • 分数可以精确的表示的整数的范围:Redis 有序集合的分数使用双精度64位浮点数。我们支持所有的架构,这表示为一个IEEE 754 floating point number,它能包括的整数范围是-(2^53) 到 +(2^53)。或者说是-9007199254740992 到 9007199254740992。更大的整数在内部用指数形式表示,所以,如果为分数设置一个非常大的整数,你得到的是一个近似的十进制数
  • 返回值
    • 添加到有序集合的成员数量,不包括已经存在更新分数的成员
    • 成员的新分数(双精度的浮点型数字)字符串
  • 复杂度:O(log(N)) for each item added, where N is the number of elements in the sorted set
#案例:创业公司招进了4个员工,分别为: alex 工资2000元  tom工资5000元 jack工资6000元 阿甘1000元,请按工资升序排序
39.100.196.99:6379> zadd salary 2000  alex  5000 tom 6000 jack 1000 agan
(integer) 4
39.100.196.99:6379> zrange salary 0 -1 withscores
1) "agan"
2) "1000"
3) "alex"
4) "2000"
5) "tom"
6) "5000"
7) "jack"
8) "6000"
(1.21s)
1.1 相同分数的成员

有序集合里面的成员是不能重复的都是唯一的,但是,不同成员间有可能有相同的分数。当多个成员有相同的分数时,他们将是有序的字典(ordered lexicographically)(仍由分数作为第一排序条件,然后,相同分数的成员按照字典规则相对排序)。

字典顺序排序用的是二进制,它比较的是字符串的字节数组。

如果用户将所有元素设置相同分数(例如0),有序集合里面的所有元素将按照字典顺序进行排序,范围查询元素可以使用ZRANGEBYLEX命令(注:范围查询分数可以使用ZRANGEBYSCORE命令)

2.zscore:返回元素分数

  • 格式zscore key member
  • 如果member元素不是有序集key的成员,或key不存在,返回nil
  • 返回值:member成员的score值(double型浮点数),以字符串形式表示
  • 复杂度:O(1)
# 案例:创业公司老板问你 ,阿甘的工资是多少 ?
39.100.196.99:6379> zrange salary 0 -1 withscores
1) "agan"
2) "1000"
3) "alex"
4) "2000"
5) "tom"
6) "5000"
7) "jack"
8) "6000"
39.100.196.99:6379> zscore salary agan
"1000"

3.zrank:返回有序集合中member的排名

  • 格式zrank key member
  • 其中有序集成员按score值递增(从小到大)顺序排列。排名以0为底,也就是说,score值最小的成员排名为0。
  • 使用ZREVRANK命令可以获得成员按score值递减(从大到小)排列的排名
  • 返回值
    • 如果member是有序集key的成员,返回integer-reply:member的排名。
    • 如果member不是有序集key的成员,返回bulk-string-reply: nil
  • 复杂度:O(log(N))
# 案例:创业公司老板要查,工资从低到高,查某个员工排第几名?
9.100.196.99:6379> ZREVRANGE salary 0 -1 withscores
1) "jack"
2) "6000"
3) "tom"
4) "5000"
5) "alex"
6) "2000"
7) "agan"
8) "1500"
(0.75s)
39.100.196.99:6379> ZRANK salary agan
(integer) 0

4.zrevrank:返回有序集合中member的排名(从大到小)

  • 格式:zrevrank key member
  • 其中有序集成员按score值递减(从大到小)顺序排列。排名以0为底,也就是说,score值最大的成员排名为0。
  • 使用ZRANK命令可以获得成员按score值递增(从小到大)排列的排名
  • 返回值
    • 如果member是有序集key的成员,返回integer-reply:member的排名。
    • 如果member不是有序集key的成员,返回bulk-string-reply: nil
  • 复杂度:O(log(N))
redis> ZADD myzset 1 "one"
(integer) 1
redis> ZADD myzset 2 "two"
(integer) 1
redis> ZADD myzset 3 "three"
(integer) 1
redis> ZREVRANK myzset "one"
(integer) 2
redis> ZREVRANK myzset "four"
(nil)

5.zpopmax:删除并返回有序集合key中的根据分数从大到小排名前count个成员

  • 格式zpopmax key [count]
  • 如未指定,count的默认值为1。指定一个大于有序集合的基数的count不会产生错误。 当返回多个元素时候,得分最高的元素将是第一个元素,然后是分数较低的元素
  • 返回值:弹出的元素和分数列表
  • 复杂度:O(log(N)*M) with N being the number of elements in the sorted set, and M being the number of elements popped
redis> ZADD myzset 1 "one"
(integer) 1
redis> ZADD myzset 2 "two"
(integer) 1
redis> ZADD myzset 3 "three"
(integer) 1
redis> ZPOPMAX myzset
1) "3"
2) "three"

# 。。。

aliyunYHX:0>ZRANGE myzset 0 -1 WITHSCORES
 1)  "one"
 2)  "1"
 3)  "tWo"
 4)  "2"
 5)  "three"
 6)  "3"
 7)  "four"
 8)  "4"
 9)  "fourfor"
 10)  "4"
 11)  "fourforfor"
 12)  "4"
 13)  "fourforforfor"
 14)  "4"
aliyunYHX:0>zpopmax myzset
 1)  "fourforforfor"
 2)  "4"
aliyunYHX:0>zpopmax myzset 5
 1)  "fourforfor"
 2)  "4"
 3)  "fourfor"
 4)  "4"
 5)  "four"
 6)  "4"
 7)  "three"
 8)  "3"
 9)  "tWo"
 10)  "2"
aliyunYHX:0>ZRANGE myzset 0 -1 WITHSCORES
 1)  "one"
 2)  "1"
aliyunYHX:0>

6.bzpopmax:bzpopmax是有序集合命令 ZPOPMAX带有阻塞功能的版本

  • 复杂度bzpopmax key [key ...] timeout
  • 在参数中的所有有序集合均为空的情况下,阻塞连接。参数中包含多个有序集合时,按照参数中key的顺序,返回第一个非空key中分数最大的成员和对应的分数
  • 参数 timeout 可以理解为客户端被阻塞的最大秒数值,0 表示永久阻塞。
  • 详细说明请参照BZPOPMIN 说明文档,BZPOPMAX返回非空有序集合 key中分数最大的成员,而BZPOPMIN返回该key中分数最小的成员,除此之外,两条命令无其他差别
  • 返回值:当有序集合无结果返回且超时设置过期时返回 nil;返回三元素multi-bulk结果,第一元素key名称,第二元素成员名称,第三元素分数
  • 复杂度:O(log(N)) with N being the number of elements in the sorted set.
redis> DEL zset1 zset2
(integer) 0
redis> ZADD zset1 0 a 1 b 2 c
(integer) 3
redis> BZPOPMAX zset1 zset2 0
1) "zet1"
2) "2"
2) "c"

7.bzpopmin:是有序集合命令 zpopmin带有阻塞功能的版本

  • 复杂度:bzpopmax key [key …] timeout
  • 在参数中的所有有序集合均为空的情况下,阻塞连接。参数中包含多个有序集合时,按照参数中key的顺序,返回第一个非空key中分数最大的成员和对应的分数
  • 参数 timeout 可以理解为客户端被阻塞的最大秒数值,0 表示永久阻塞。
  • 详细说明请参照BLPOP 说明文档,BZPOPMIN适用有序集合类型的key,BLPOP适用列表类型的key,除此之外,两条命令无其他差别
  • 返回值:当有序集合无结果返回且超时设置过期时返回 nil;返回三元素multi-bulk结果,第一元素key名称,第二元素成员名称,第三元素分数
  • 复杂度:O(log(N)) with N being the number of elements in the sorted set.
redis> DEL zset1 zset2
(integer) 0
redis> ZADD zset1 0 a 1 b 2 c
(integer) 3
redis> BZPOPMIN zset1 zset2 0
1) "zet1"
2) "0"
2) "a"

8.zpopmin:删除并返回有序集合key中的根据分数从小到大排名前count个成员

  • 格式zpopmin key [count]
  • 如未指定,count的默认值为1。指定一个大于有序集合的基数的count不会产生错误。 当返回多个元素时候,得分最低的元素将是第一个元素,然后是分数较高的元素
  • 返回值:弹出的元素和分数列表
  • 复杂度:O(log(N)*M) with N being the number of elements in the sorted set, and M being the number of elements popped
redis> ZADD myzset 1 "one"
(integer) 1
redis> ZADD myzset 2 "two"
(integer) 1
redis> ZADD myzset 3 "three"
(integer) 1
redis> ZPOPMIN myzset
1) "1"
2) "one"

9.zrem:删除有序集合中的元素

  • 格式zrem key member1 [member2....]
  • 返回值:返回的是从有序集合中删除的成员个数,不包括不存在的成员
  • 当key存在,但是其不是有序集合类型,就返回一个错误
# 案例:创业公司 tom离职了
39.100.196.99:6379> zrange salary 0 -1 withscores
1) "agan"
2) "1000"
3) "alex"
4) "2000"
5) "tom"
6) "5000"
7) "jack"
8) "6000"
(1.21s)
39.100.196.99:6379> zrem salary tom
(integer) 1
39.100.196.99:6379> zrange salary 0 -1 withscores
1) "agan"
2) "1000"
3) "alex"
4) "2000"
5) "jack"
6) "6000"

10.zremrangebylex:移除有序集合中给定的字典区间的所有成员

  • 格式ZREMRANGEBYLEX key min max
  • 不要在成员分数不同的有序集合中使用此命令, 因为它是基于分数一致的有序集合设计的,如果使用,会导致删除的结果不正确。
  • 待删除的有序集合中,分数最好相同,否则删除结果会不正常
  • 参数:
    • min:字典中排序位置较小的成员,必须以"[“开头,或者以”(“开头,可使用”-"代替
    • max:字典中排序位置较大的成员,必须以"[“开头,或者以”(“开头,可使用”+"代替
  • 提示
    • 有序集合中分数必须相同! 如果有序集合中的成员分数有不一致的,结果就不准。
    • 成员顺序是按成员字符串作为二进制数组的字节数进行比较。
    • 默认是以ASCII字符集的顺序进行排列。如果成员字符串包含utf-8这类字符集的内容,就会影响返回结果,所以建议不要使用。
    • 源码中采用C语言中 memcmp() 函数, 从字符的第0位到最后一位进行排序,如果前面部分相同,那么较长的字符串比较短的字符串排序靠后。
    • 默认情况下, “max” 和 “min” 参数前必须加 “[” 符号作为开头。”[” 符号与成员之间不能有空格, 返回成员结果集会包含参数 “max”和 “min”
    • “max” 和 “min” 参数前可以加 “(“ 符号作为开头表示小于, “(“ 符号与成员之间不能有空格。返回成员结果集不会包含 “max” 和 “min” 成员。
    • 可以使用 “-“ 和 “+” 表示得分最小值和最大值
    • “max”和 “min” 不能反, “max” 放后面 “min”放前面会删除不了元素
  • 返回值:删除元素个数
  • 复杂度:O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements removed by the operation
10.1 示例1:删除所有元素

不要在分数不一致的SortSet集合中去使用 ZREMRANGEBYLEX 指令,因为获取的结果并不准确。

可以使用 “-“ 和 “+” 表示最小值和最大值

redis> zadd zset 0 a 0 aa 0 abc 0 apple 0 b 0 c 0 d 0 d1 0 dd 0 dobble 0 z 0 z1
(integer) 12
redis> ZRANGEBYLEX zset + -
 1) "a"
 2) "aa"
 3) "abc"
 4) "apple"
 5) "b"
 6) "c"
 7) "d"
 8) "d1"
 9) "dd"
10) "dobble"
11) "z"
12) "z1"
redis> ZREMRANGEBYLEX zset - +
(integer) 7
redis> ZRANGEBYLEX zset - +
(empty list or set)
10.2 示例2:按名称删除某个元素

下面是删除d1这个元素

redis> zadd zset 0 a 0 aa 0 abc 0 apple 0 b 0 c 0 d 0 d1 0 dd 0 dobble 0 z 0 z1
(integer) 12
redis> ZRANGEBYLEX zset - +
 1) "a"
 2) "aa"
 3) "abc"
 4) "apple"
 5) "b"
 6) "c"
 7) "d"
 8) "d1"
 9) "dd"
10) "dobble"
11) "z"
12) "z1"
redis> ZREMRANGEBYLEX zset [d1 (dd
(integer) 1
redis> ZRANGEBYLEX zset - +
 1) "a"
 2) "aa"
 3) "abc"
 4) "apple"
 5) "b"
 6) "c"
 7) "d"
 8) "dd"
 9) "dobble"
10) "z"
11) "z1"
10.3 示例3:按名称删除成员之间的元素,包含”max” 和 “min”成员

默认情况下, “max” 和 “min” 参数前必须加 “[” 符号作为开头。
“[” 符号与成员之间不能有空格, 删除成员包含参数 “min” 和 “max” 。

redis> ZRANGEBYLEX zset - +
 1) "a"
 2) "aa"
 3) "abc"
 4) "apple"
 5) "b"
 6) "c"
 7) "d"
 8) "dd"
 9) "dobble"
10) "z"
11) "z1"
redis> ZREMRANGEBYLEX zset [a [apple
(integer) 4
redis> ZRANGEBYLEX zset - +
1) "b"
2) "c"
3) "d"
4) "dd"
5) "dobble"
6) "z"
7) "z1"

“min” 和 “max” 不能反, “min” 放前面 “max”放后面会导致无法删除元素

redis> ZREMRANGEBYLEX zset [dobble [d
(integer) 0
10.4 示例4:按名称删除成员之间的元素,不包含”max” 和 “min”成员

“max” 和 “min” 参数前可以加 “(“ 符号作为开头表示小于, “(“ 符号与成员之间不能有空格。
删除成员不会包含 “max” 和 “min” 成员。

redis> ZRANGEBYLEX zset - +
1) "b"
2) "c"
3) "d"
4) "dd"
5) "dobble"
6) "z"
7) "z1"     
redis> ZREMRANGEBYLEX zset (d (dobble
(integer) 1
redis> ZRANGEBYLEX zset - +
1) "b"
2) "c"
3) "d"
4) "dobble"
5) "z"
6) "z1"

11.zremrangebyrank:移除有序集key中,指定排名(rank)区间内的所有成员(包含两端)

  • 格式ZREMRANGEBYRANK key start stop
  • 下标参数start和stop都以0为底,0处是分数最小的那个元素。这些索引也可是负数,表示位移从最高分处开始数。例如,-1是分数最高的元素,-2是分数第二高的,依次类推
  • 返回值:删除元素个数
  • 复杂度:O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements removed by the operation
# 案例:经济不好,老板要裁员了,把工资最低的2个人裁掉
39.100.196.99:6379> zrange salary 0 -1 withscores
1) "agan"
2) "1500"
3) "alex"
4) "2000"
5) "tom"
6) "5000"
7) "jack"
8) "6000"
39.100.196.99:6379> ZREMRANGEBYRANK salary 0 1
(integer) 2
39.100.196.99:6379> zrange salary 0 -1 withscores
1) "tom"
2) "5000"
3) "jack"
4) "6000"

12.zremrangebyscore:移除有序集key中,所有score值介于min和max之间(包括等于min或max)的成员。

  • 格式zremrangebyscore key min max
  • 自版本2.1.6开始,score值等于min或max的成员也可以不包括在内,语法请参见ZRANGEBYSCORE命令
  • 返回值:删除元素个数
  • 复杂度:O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements removed by the operation
redis> ZADD myzset 1 "one"
(integer) 1
redis> ZADD myzset 2 "two"
(integer) 1
redis> ZADD myzset 3 "three"
(integer) 1
redis> ZREMRANGEBYSCORE myzset -inf (2
(integer) 1
redis> ZRANGE myzset 0 -1 WITHSCORES
1) "two"
2) "2"
3) "three"
4) "3"

13.zcard:返回有序集合中元素个数

  • 格式zcard key
  • 返回值: key存在的时候,返回有序集的元素个数,否则返回0
  • 复杂度:O(1)
# 案例:创业公司 有多少人
39.100.196.99:6379> zrange salary 0 -1 withscores
1) "agan"
2) "1000"
3) "alex"
4) "2000"
5) "jack"
6) "6000"
39.100.196.99:6379> zcard salary
(integer) 3


14.zcount:返回有序集合中分数值在指定范围内的元素个数

  • 格式zcount key min max
  • 返回值:指定分数范围的元素个数
  • 复杂度:O(log(N)) with N being the number of elements in the sorted set
# 案例:创业公司老板问你 ,工资在2000 至 6000有多少人
39.100.196.99:6379> zrange salary 0 -1 withscores
1) "agan"
2) "1000"
3) "alex"
4) "2000"
5) "tom"
6) "5000"
7) "jack"
8) "6000"
39.100.196.99:6379> ZCOUNT salary 2000 6000
(integer) 3

15.zrange:返回存储在有序集合key中的指定范围的元素

  • 格式ZRANGE key start stop [WITHSCORES]
  • 返回的元素可以认为是按得分从最低到最高排列。 如果得分相同,将按字典排序。
  • 当你需要元素从最高分到最低分排列时,请参阅ZREVRANGE(相同的得分将使用字典倒序排序)。
  • 参数start和stop都是基于零的索引,即0是第一个元素,1是第二个元素,以此类推。 它们也可以是负数,表示从有序集合的末尾的偏移量,其中-1是有序集合的最后一个元素,-2是倒数第二个元素,等等。
  • start和stop都是全包含的区间,因此例如ZRANGE myzset 0 1将会返回有序集合的第一个和第二个元素。
  • 超出范围的索引不会产生错误。 如果start参数的值大于有序集合中的最大索引,或者start > stop,将会返回一个空列表。 如果stop的值大于有序集合的末尾,Redis会将其视为有序集合的最后一个元素。
  • 可以传递WITHSCORES选项,以便将元素的分数与元素一起返回。这样,返回的列表将包含value1,score1,…,valueN,scoreN,而不是value1,…,valueN。 客户端类库可以自由地返回更合适的数据类型(建议:具有值和得分的数组或记录)
  • 返回值:给定范围内的元素列表(如果指定了WITHSCORES选项,将同时返回它们的得分)
  • 复杂度:O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements returned
redis 127.0.0.1:6379> ZRANGE salary 0 -1 WITHSCORES             # 显示整个有序集成员
1) "jack"
2) "3500"
3) "tom"
4) "5000"
5) "boss"
6) "10086"
 
redis 127.0.0.1:6379> ZRANGE salary 1 2 WITHSCORES              # 显示有序集下标区间 1 至 2 的成员
1) "tom"
2) "5000"
3) "boss"
4) "10086"
 
redis 127.0.0.1:6379> ZRANGE salary 0 200000 WITHSCORES         # 测试 end 下标超出最大下标时的情况
1) "jack"
2) "3500"
3) "tom"
4) "5000"
5) "boss"
6) "10086"
 
redis > ZRANGE salary 200000 3000000 WITHSCORES                  # 测试当给定区间不存在于有序集时的情况
(empty list or set)

16.zrevrange:返回有序集key中,指定区间内的成员(从大到小)

  • 格式ZREVRANGE key start stop [WITHSCORES]
  • 其中成员的位置按score值递减(从大到小)来排列。具有相同score值的成员按字典序的反序排列。
  • 除了成员按score值递减的次序排列这一点外,ZREVRANGE命令的其他方面和ZRANGE命令一样
  • 返回值:给定范围内的元素列表(如果指定了WITHSCORES选项,将同时返回它们的得分)
  • 复杂度:O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements returned
# 案例:创业公司老板说经济不好,成本太大,看工资最多的是哪些人?
39.100.196.99:6379> zrange salary 0 -1 withscores  #升序
1) "agan"
2) "1500"
3) "alex"
4) "2000"
5) "tom"
6) "5000"
7) "jack"
8) "6000"
39.100.196.99:6379> ZREVRANGE salary 0 -1 withscores #降序
1) "jack"
2) "6000"
3) "tom"
4) "5000"
5) "alex"
6) "2000"
7) "agan"
8) "1500"

17.zrangebylex:返回指定成员区间内的成员,按成员字典正序排序, 分数必须相同

  • 格式ZRANGEBYLEX key min max [LIMIT offset count]
  • 在某些业务场景中,需要对一个字符串数组按名称的字典顺序进行排序时,可以使用Redis中SortSet这种数据结构来处理
  • 参数说明
    • min:字典中排序位置较小的成员,必须以"[“开头,或者以”(“开头,可使用”-"代替
    • max:字典中排序位置较大的成员,必须以"[“开头,或者以”(“开头,可使用”+"代替
    • limit:返回结果是否分页,指令中包含LIMIT后offset、count必须输入
    • offset:返回结果起始位置
    • count:返回结果数量
  • 提示
    • 分数必须相同! 如果有序集合中的成员分数有不一致的,返回的结果就不准。
    • 成员字符串作为二进制数组的字节数进行比较。
    • 默认是以ASCII字符集的顺序进行排列。如果成员字符串包含utf-8这类字符集的内容,就会影响返回结果,所以建议不要使用。
    • 默认情况下, “max” 和 “min” 参数前必须加 “[” 符号作为开头。”[” 符号与成员之间不能有空格, 返回成员结果集会包含参数 “min” 和 “max” 。
    • “max” 和 “min” 参数前可以加 “(“ 符号作为开头表示小于, “(“ 符号与成员之间不能有空格。返回成员结果集不会包含 “max” 和 “min” 成员。
    • 可以使用 “-“ 和 “+” 表示得分最小值和最大值
    • “min” 和 “max” 不能反, “max” 放前面 “min”放后面会导致返回结果为空
    • 与ZRANGEBYLEX获取顺序相反的指令是ZREVRANGEBYLEX。
    • 源码中采用C语言中 memcmp() 函数, 从字符的第0位到最后一位进行排序,如果前面部分相同,那么较长的字符串比较短的字符串排序靠后
  • 返回值:指定成员范围内的元素列表
  • 强调:不要在分数不一致的SortSet集合中去使用 ZRANGEBYLEX 指令,因为获取的结果并不准确
  • 复杂度:O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N))
17.1 示例1: 获取所有值

可以使用 “-“ 和 “+” 表示得分最小值和最大值

redis> zadd zset 0 a 0 aa 0 abc 0 apple 0 b 0 c 0 d 0 d1 0 dd 0 dobble 0 z 0 z1
(integer) 12
redis> ZRANGEBYLEX zset - +
 1) "a"
 2) "aa"
 3) "abc"
 4) "apple"
 5) "b"
 6) "c"
 7) "d"
 8) "d1"
 9) "dd"
10) "dobble"
11) "z"
12) "z1"
17.2 示例2:获取分页数据
redis> ZRANGEBYLEX zset - + LIMIT 0 3
1) "a"
2) "aa"
3) "abc"
redis> ZRANGEBYLEX zset - + LIMIT 3 3
1) "apple"
2) "b"
3) "c"
17.3 示例3: 获取成员之间的元素

默认情况下, “max” 和 “min” 参数前必须加 “[” 符号作为开头。
“[” 符号与成员之间不能有空格, 返回成员结果集会包含参数 “min” 和 “max”

redis> ZRANGEBYLEX zset [aa [c
1) "aa"
2) "abc"
3) "apple"
4) "b"
5) "c"

“min” 和 “max” 不能反, “max” 放前面 “min”放后面会导致返回结果为空

redis> ZRANGEBYLEX zset [c [aa
(empty list or set)
17.4 示例4:使用 “(“ 小于号获取成员之间的元素

“max” 和 “min” 参数前可以加 “(“ 符号作为开头表示小于, “(“ 符号与成员之间不能有空格。
返回成员结果集不会包含 “max” 和 “min” 成员

redis> ZRANGEBYLEX zset [aa (c
1) "aa"
2) "abc"
3) "apple"
4) "b"
17.5 示例5:ASCII码的影响

成员字符串作为二进制数组的字节数进行比较。
默认是以ASCII字符集的顺序进行排列。
如果成员字符串包含utf-8这类字符集的内容,就会影响返回结果,所以建议不要使用

redis> zadd zset 0 aBc
(integer) 1
redis> ZRANGEBYLEX zset - +
 1) "a"
 2) "aBc"
 3) "aa"
 4) "abc"
 5) "apple"
 6) "b"
 7) "c"
 8) "d"
 9) "d1"
10) "dd"
11) "dobble"
12) "z"
13) "z1"
redis> ZRANGEBYLEX zset - + LIMIT 0 3
1) "a"
2) "aBc"
3) "aa"
17.6 应用1:电话号码排序

我们可以将电话号码存储到SortSet中,然后根据需要来获取号段:

redis> zadd phone 0 13100111100 0 13110114300 0 13132110901 
(integer) 3
redis> zadd phone 0 13200111100 0 13210414300 0 13252110901 
(integer) 3
redis> zadd phone 0 13300111100 0 13310414300 0 13352110901 
(integer) 3

获取所有号码:

redis> ZRANGEBYLEX phone - +
1) "13100111100"
2) "13110114300"
3) "13132110901"
4) "13200111100"
5) "13210414300"
6) "13252110901"
7) "13300111100"
8) "13310414300"
9) "13352110901"

获取132号段:

redis> ZRANGEBYLEX phone [132 (133
1) "13200111100"
2) "13210414300"
3) "13252110901"

获取132、133号段

redis> ZRANGEBYLEX phone [132 (134
1) "13200111100"
2) "13210414300"
3) "13252110901"
4) "13300111100"
5) "13310414300"
6) "13352110901"
17.7 应用2:姓名排序

将名称存储到SortSet中:

redis> zadd names 0 Toumas 0 Jake 0 Bluetuo 0 Gaodeng 0 Aimini 0 Aidehua 
(integer) 6

获取所有人的名字:

redis> ZRANGEBYLEX names - +
1) "Aidehua"
2) "Aimini"
3) "Bluetuo"
4) "Gaodeng"
5) "Jake"
6) "Toumas"

获取名字中大写字母A开头的所有人:

redis> ZRANGEBYLEX names [A (B
1) "Aidehua"
2) "Aimini"

获取名字中大写字母C到Z的所有人:

redis> ZRANGEBYLEX names [C [Z
1) "Gaodeng"
2) "Jake"
3) "Toumas"

18.zrevrangebylex:返回指定成员区间内的成员,按成员字典正序排序, 分数必须相同

与zrangebylex相反

19.zrangebyscore:返回key的有序集合中的分数在min和max之间的所有元素(包括分数等于max或者min的元素)

  • 格式ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
  • 元素被认为是从低分到高分排序的。
  • 具有相同分数的元素按字典序排列(这个根据redis对有序集合实现的情况而定,并不需要进一步计算)。
  • 可选的LIMIT参数指定返回结果的数量及区间(类似SQL中SELECT LIMIT offset, count)。注意,如果offset太大,定位offset就可能遍历整个有序集合,这会增加O(N)的复杂度。
  • 可选参数WITHSCORES会返回元素和其分数,而不只是元素。这个选项在redis2.0之后的版本都可用。
  • 区间及无限
    • min和max可以是-inf和+inf,这样一来,你就可以在不知道有序集的最低和最高score值的情况下,使用ZRANGEBYSCORE这类命令。
    • 默认情况下,区间的取值使用闭区间(小于等于或大于等于),你也可以通过给参数前增加(符号来使用可选的开区间(小于或大于)。
  • 返回值:指定分数范围的元素列表(也可以返回他们的分数)
  • 复杂度:O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).
    如果M是常量(比如,用limit总是请求前10个元素),你可以认为是O(log(N))
#案例:创业公司老板要给工资低的人加薪水,老板要求先看低于5000元的有哪些人?人多的话分页查看
39.100.196.99:6379> ZREVRANGE salary 0 -1 withscores
1) "jack"
2) "6000"
3) "tom"
4) "5000"
5) "alex"
6) "2000"
7) "agan"
8) "1500"
39.100.196.99:6379> ZRANGEBYSCORE salary 1 5000
1) "agan"
2) "alex"
3) "tom"
39.100.196.99:6379> ZRANGEBYSCORE salary 1 5000 LIMIT 0 2
1) "agan"
2) "alex"
39.100.196.99:6379> ZRANGEBYSCORE salary 1 5000 LIMIT 2 2
1) "tom"


20.zrevrangebyscore:返回有序集合中指定分数区间内的成员,分数由高到低排序

与ZRANGEBYSCORE 相反

  • 格式ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count]
  • 参数
    • max:最大分数值,可使用"+inf"代替
    • min:最小分数值,可使用"-inf"代替
    • withscores:将成员分数一并返回
    • limit:返回结果是否分页,指令中包含LIMIT后offset、count必须输入
    • offset:返回结果起始位置
    • count:返回结果数量
  • 提示
    • “max” 和 "min"参数前可以加 “(” 符号作为开头表示小于, “(” 符号与成员之间不能有空格
    • 可以使用 “+inf” 和 “-inf” 表示得分最大值和最小值
    • “max” 和 “min” 不能反, “max” 放后面 "min"放前面会导致返回结果为空
    • 计算成员之间的成员数量不加 “(” 符号时,参数 “min” 和 “max” 的位置也计算在内。
    • ZREVRANGEBYSCORE集合中按得分从高到底排序,所以"max"在前面,"min"在后面, ZRANGEBYSCORE集合中按得分从底到高排序,所以"min"在前面,"max"在后面。
  • 返回值:指定分数范围的元素列表
  • 复杂度:O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N))
20.1 示例1:按分数倒序返回成员

“+inf” 或者 “-inf” 来表示记录中最大值和最小值。 “(” 左括号来表示小于某个值。目前只支持小于操作的 “(” 左括号, 右括号(大于)目前还不能支持。

redis> ZADD myzset 1 "one"
(integer) 1
redis> ZADD myzset 2 "two"
(integer) 1
redis> ZADD myzset 3 "three"
(integer) 1
redis> ZREVRANGEBYSCORE myzset +inf -inf
1) "three"
2) "two"
3) "one"
redis> ZREVRANGEBYSCORE myzset 2 1
1) "two"
2) "one"
redis> ZREVRANGEBYSCORE myzset 2 (1
1) "two"
redis> ZREVRANGEBYSCORE myzset (2 (1
(empty list or set)
redis> 
20.2 示例2: 按分数倒序返回成员以及分数

ZREVRANGEBYSCORE 指令中, 还可以使用WITHSCORES 关键字来要求返回成员列表以及分数。

redis> ZADD myzset 1 "one"
(integer) 1
redis> ZADD myzset 2 "two"
(integer) 1
redis> ZADD myzset 3 "three"
(integer) 1
redis> ZREVRANGEBYSCORE myzset +inf -inf WITHSCORES
1) "three"
2) "3"
3) "two"
4) "2"
5) "three"
6) "1"
redis> ZREVRANGEBYSCORE myzset 2 1 WITHSCORES
1) "two"
2) "2"
3) "one"
4) "1"
redis> ZREVRANGEBYSCORE myzset 2 (1
1) "two"
redis> ZREVRANGEBYSCORE myzset (2 (1
(empty list or set)
redis> 
20.3 应用3:分页返回数据

可以通过 LIMIT 对满足条件的成员列表进行分页。一般会配合 “+inf” 或者 “-inf” 来表示最大值和最小值。 下面的例子中就是使用分页参数返回数据的例子。

redis> ZADD myzset 1 "one"
(integer) 1
redis> ZADD myzset 2 "two"
(integer) 1
redis> ZADD myzset 3 "three"
(integer) 1
redis> ZREVRANGEBYSCORE myzset +inf (2 WITHSCORES LIMIT 0 1 
1) "three"
2) "3"
redis> ZREVRANGEBYSCORE myzset +inf (2 WITHSCORES LIMIT 2 3
(empty list or set)
20.4 应用1:时事新闻

我们的网易新闻、腾讯新闻、一点资讯、虎嗅、快科技等新闻类应用场景中, 新闻会根据时间排列,形成一个队列, 在这些网站浏览新闻时, 每次刷新页面都能获取到最新的数据,当然还可以通过分页查看历史数据, 但每次刷新首页都将是一个以最新数据为基础进行分页。 这又区别于面前今日头条的下拉刷新机制,关于今日头条的下拉刷新机制我们下节讲,这一节先讲讲如何实现新闻资讯队列分页。

场景及需求:

根据数据类型分频道来展示资讯,比如时事新闻,根据时间排序,用户进入网站就获取到最新时事新闻,后台能实时向时事新闻频道增加新闻,且只要用户刷新就能看到最新的时事新闻,然后以最新新闻为基础进行分页展示。 我们根据时间添加5条新闻到news频道。

图片1

redis> zadd news 201610022301 '{"title" : "new1", "time": 201610022301}'
(integer) 1
redis> zadd news 201610022302 '{"title" : "new2", "time": 201610022302}'
(integer) 1
redis> zadd news 201610022303 '{"title" : "new3", "time": 201610022303}'
(integer) 1
redis> zadd news 201610022304 '{"title" : "new4", "time": 201610022304}'
(integer) 1
redis> zadd news 201610022305 '{"title" : "new5", "time": 201610022305}'

实际场景中new1、news2…等等都应该是一个json对象,包含新闻标题、时间、作者、类型、图片URL…等等信息。 然后当用户请求新闻时,我们会使用这个命令查询出最新几条记录:

redis> ZREVRANGEBYSCORE news +inf -inf LIMIT 0 2
1) "{\"title\" : \"new5\", \"time\": 201610022305}"
2) "{\"title\" : \"new4\", \"time\": 201610022304}"

上面的例子中,我们从news频道查询出来了2条最新记录。如果用户翻页,我们会使用最新记录中时间最大的记录做为参数进行分页。依次查询第二页,第三页。

redis> ZREVRANGEBYSCORE news 201610022305 -inf LIMIT 2 2
1) "{\"title\" : \"new3\", \"time\": 201610022303}"
2) "{\"title\" : \"new2\", \"time\": 201610022302}"
redis> ZREVRANGEBYSCORE news 201610022305 -inf LIMIT 4 2
1) "{\"title\" : \"new1\", \"time\": 201610022301}"
redis> 

总结: 我这里没有使用当前时间作为最大值查询,是为了避免用户电脑时间不准确导致请求失败。比如刚装系统,或者BOIS电池没有电,不能存储系统当前时间,就会导致系统时间都是1970年1月1日,这样,去查询,是查询不到数据的,比如之前的太平洋电脑网,如果你将电脑系统时间改成1970年1月1日,再去访问,就无法获取新数据了。现在有不少网站有类似问题. 在这个业务场景中,如果某个编辑新发布一条新闻到时事新闻中,原来在翻页查看新闻的用户是不受影响的,只有用户刷新浏览器或者刷新首页,就会看到最新的数据了。

20.5 应用2:时事新闻下拉更新

上面时事新闻的例子能解决一部分业务场景的需求,但是如果遇到比较较真的产品经理,需要做成类似今日头条下拉刷新最新数据,上拉获取历史记录的功能,那么一个 ZREVRANGEBYSCORE 命令肯定是解决不了问题的。下面我讲讲我一些解决方案的思路。
首先,从队列的角度看需求,以201610022303这个时间为界限,下拉刷新如果获最新数据,就是这样:

redis> ZREVRANGEBYSCORE news +inf 201610022303 LIMIT 0 2
1) "{\"title\" : \"new5\", \"time\": 201610022305}"
2) "{\"title\" : \"new4\", \"time\": 201610022304}"

然后,上拉刷新获取历史数据,分页查询,都没有问题:

redis> ZREVRANGEBYSCORE news 201610022303 -inf LIMIT 0 2
1) "{\"title\" : \"new3\", \"time\": 201610022303}"
2) "{\"title\" : \"new2\", \"time\": 201610022302}"
redis> ZREVRANGEBYSCORE news 201610022303 -inf LIMIT 2 2
1) "{\"title\" : \"new1\", \"time\": 201610022301}"
redis> 

不过,你会发现,下拉刷新时,数据是从最大时间到最小时间排序,如果编辑新增一条数据,就会打乱我们队列的顺序

redis> zadd news 201610022306 '{"title" : "new6", "time": 201610022306}'
(integer) 1
redis> ZREVRANGEBYSCORE news +inf 201610022303 LIMIT 2 2
1) "{\"title\" : \"new4\", \"time\": 201610022304}"
2) "{\"title\" : \"new3\", \"time\": 201610022303}"
redis> 

前一秒,查询第一页数据是news5和news4,我翻页的时候,结果变成了news4和news3,news4重复了。 虽然不会对上拉刷新查询历史数据有影响,但是作为一个实时性非常强的新闻应用,用户更关注的应该是最新的新闻的内容,也就是下拉刷新的功能,如果一个基本的排版都重复,用户的耐心恐怕不会给你更多机会。 想到这,感觉太可怕了!产品经理要是知道,一定会喷死我们的。当然,这不是最要命的问题,最要命的问题是如果用户要是回到第一页(非刷新),就会发现,当初的第一页已经不是刚刚看过的第一页了。 第一页多了一条news6的新闻!

edis> ZREVRANGEBYSCORE news +inf 201610022303 LIMIT 0 2
1) "{\"title\" : \"new6\", \"time\": 201610022306}"
2) "{\"title\" : \"new5\", \"time\": 201610022305}"

还有一个问题,就是如果用户前一天看到第二页,隔天登录,然后请求最新新闻,还会是第二页吗? 可以考虑以下解决思路:
首先,需要前端和后端配合,前端定义三个动作,首次登录,下拉刷新(获取最新数据),上拉刷新(获取历史数据),定义一个下拉刷新时间和一个上拉刷新时间, 如果是首次登录,先判断下拉刷新时间是否比今天凌晨时间小,如果小,证明已经隔天了,将下拉刷新时间设置成当天凌晨时间; 如果下拉刷新时间比今天凌晨时间大,说明用户看过今天新闻,就直接用下拉刷新时间请求新数据。 对于上拉刷新请求数据,可以在判断首次请求时,出现隔天后,将上拉刷新时间置为当天凌晨时间。这样在请求历史数据时,不至于丢失中间的数据。 当然,关键还是命令的使用,查询历史数据时,还可以使用 ZREVRANGEBYSCORE 来获取数据,不过,获取最新数据,就可以使用 ZRANGEBYSCORE 来取数据。
在这里插入图片描述

redis> zadd news 201610030110 '{"title" : "new7", "time": 201610030110}'
(integer) 1
redis> zadd news 201610030111 '{"title" : "new8", "time": 201610030111}'
(integer) 1
redis> zadd news 201610030112 '{"title" : "new9", "time": 201610030112}'
(integer) 1
redis> ZRANGEBYSCORE news 201610030000 +inf LIMIT 0 2
1) "{\"title\" : \"new7\", \"time\": 201610030110}"
2) "{\"title\" : \"new8\", \"time\": 201610030111}"
redis> ZRANGEBYSCORE news 201610030000 +inf LIMIT 2 2
1) "{\"title\" : \"new9\", \"time\": 201610030112}"

总结:时事新闻下拉更新这个例子中, 可以使用每次请求数据的最大时间戳和最小时间戳来作为下一次请求的输入,这里的时间戳的概念不限于时间,可以是数据库里的自增ID,然后在缓存中定义一个当天的ID,每当隔天出现,就取获取这个ID,然后通过这个ID作为当天凌晨的时间戳标记, 获取比这个ID大或者小的结果集来完成获取最新数据和历史数据的功能,这样就可以解决客户端时间不准确后获取不到数据的问题。

21.zincrby:给指定元素的分值加increment的值

  • 格式zincrby key increment member
  • 如果key中不存在member,就在key中添加一个member,score是increment(就好像它之前的score是0.0)。如果key不存在,就创建一个只含有指定member成员的有序集合。
  • 当key不是有序集类型时,返回一个错误。
  • score值必须是字符串表示的整数值或双精度浮点数,并且能接受double精度的浮点数。也有可能给一个负数来减少score的值
  • 返回值:member成员的新score值,以字符串形式表示
  • 复杂度:O(log(N)) where N is the number of elements in the sorted set
# 案例:创业公司老板说阿甘表现很好,给他加500元吧 
39.100.196.99:6379> ZINCRBY salary 500 agan
"1500"
39.100.196.99:6379> zrange salary 0 -1 withscores
1) "agan"
2) "1500"
3) "alex"
4) "2000"
5) "tom"
6) "5000"
7) "jack"
8) "6000"


22.zinterstore:计算给定的numkeys个有序集合的交集,并将结果放到destination结合中

  • 格式ZINTERSTORE destination numkeys key [key ...] [WEIGHTS weight] [SUM|MIN|MAX]
  • 在给定要计算的key和其它参数之前,必须先给定key个数(numberkeys)。
  • 默认情况下,结果中一个元素的分数是有序集合中该元素分数之和,前提是该元素在这些有序集合中都存在。因为交集要求其成员必须是给定的每个有序集合中的成员,结果集中的每个元素的分数和输入的有序集合个数相等。
  • 对于WEIGHTS和AGGREGATE参数的描述,参见命令ZUNIONSTORE。
  • 如果destination存在,就把它覆盖。
  • 返回值:结果有序集合destination中元素个数
  • 复杂度:O(NK)+O(Mlog(M)) worst case with N being the smallest input sorted set, K being the number of input sorted sets and M being the number of elements in the resulting sorted set
redis> ZADD zset1 1 "one"
(integer) 1
redis> ZADD zset1 2 "two"
(integer) 1
redis> ZADD zset2 1 "one"
(integer) 1
redis> ZADD zset2 2 "two"
(integer) 1
redis> ZADD zset2 3 "three"
(integer) 1
redis> ZINTERSTORE out 2 zset1 zset2 WEIGHTS 2 3
(integer) 2
redis> ZRANGE out 0 -1 WITHSCORES
1) "one"
2) "5"
3) "two"
4) "10"

23.zunionstore:计算给定的numkeys个有序集合的并集,并且把结果放到destination中

  • 格式ZUNIONSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]
  • 在给定要计算的key和其它参数之前,必须先给定key个数(numberkeys)。 默认情况下,结果集中某个成员的score值是所有给定集下该成员score值之和。
  • 使用WEIGHTS选项,你可以为每个给定的有序集指定一个乘法因子(各自的有序集指定各自的),意思就是,每个给定有序集的所有成员的score值在传递给聚合函数之前都要先乘以该因子。如果WEIGHTS没有给定,默认就是1。
  • 使用AGGREGATE选项,你可以指定并集的结果集的聚合方式。默认使用的参数SUM,可以将所有集合中某个成员的score值之和作为结果集中该成员的score值。如果使用参数MIN或者MAX,结果集就是所有集合中元素最小或最大的元素。
  • 如果key destination存在,就被覆盖
  • 返回值:结果有序集合destination中元素个数
  • 复杂度:O(N)+O(M log(M)) with N being the sum of the sizes of the input sorted sets, and M being the number of elements in the resulting sorted set
aliyunYHX:0>zadd zset1 1 v1
"1"
aliyunYHX:0>zadd zset1 2 v2
"1"
aliyunYHX:0>zadd zset1 3 v3
"1"
aliyunYHX:0>zadd zset2 1 v11
"1"
aliyunYHX:0>zadd zset2 2 v2
"1"
aliyunYHX:0>zadd zset2 33 v3
"1"
aliyunYHX:0>ZUNIONSTORE out 2 zset1 zset2 WEIGHTS 2 3
"4"
aliyunYHX:0>ZRANGE out 0 -1 WITHSCORES
 1)  "v1"
 2)  "2"
 3)  "v11"
 4)  "3"
 5)  "v2"
 6)  "10"
 7)  "v3"
 8)  "105"
aliyunYHX:0>ZUNIONSTORE out1 2 zset1 zset2 WEIGHTS 2 3 AGGREGATE min
"4"
aliyunYHX:0>ZRANGE out1 0 -1 WITHSCORES
 1)  "v1"
 2)  "2"
 3)  "v11"
 4)  "3"
 5)  "v2"
 6)  "4"
 7)  "v3"
 8)  "6"

24.zlexcount:计算有序集合中指定成员之间的成员数量

  • 格式zlexcount key [member1 [member2
  • 参数说明
    • member1:有序集合中分数排名较小的成员
    • member2:有序集合中分数排名较大的成员
  • 提示
    • 成员名称前需要加 [ 符号作为开头, [ 符号与成员之间不能有空格
    • 可以使用 - 和 + 表示得分最小值和最大值
    • m1 和 m2 不能反, m2 放前面 m1放后面会导致返回结果为0
    • 计算成员之间的成员数量时,参数 m1 和 m2 的位置也计算在内。
    • m1 和 m2 参数的含义与 zrangebylex 命令中所描述的相同
  • 返回值:有序集合中成员名称 m1 和 m2 之间的成员数量; Integer类型
24.1 示例1:计算成员之间成员数量
redis> ZADD myzset 0 a 0 b 0 c 0 d 0 e
(integer) 5
redis> ZADD myzset 0 f 0 g
(integer) 2
redis> ZLEXCOUNT myzset - +
(integer) 7
redis> ZLEXCOUNT myzset [b [f
(integer) 5
24.2 示例2:计算某个成员之前或者之后的成员数量

表示得分最小值的成员 + 表示得分最大值的成员

redis> ZADD myzset 1 a 2 b 3 c 4 d 5 e 6 f 7 g
(integer) 7
redis> zrange myzset 0 -1
1) "a"
2) "b"
3) "c"
4) "d"
5) "e"
6) "f"
7) "g"
redis> ZLEXCOUNT myzset - +
(integer) 7
redis> ZLEXCOUNT myzset [c +
(integer) 5
redis> ZLEXCOUNT myzset - [c
(integer) 3
24.3 示例3:分数值位置的重要性
redis> del myzset
(integer) 1
redis> ZADD myzset 1 a 2 b 3 c 4 d 5 e 6 f 7 g
(integer) 7
redis> zrange myzset 0 -1
1) "a"
2) "b"
3) "c"
4) "d"
5) "e"
6) "f"
7) "g"
redis> ZLEXCOUNT myzset [c [f
(integer) 4
redis> ZLEXCOUNT myzset [f [c
(integer) 0

25.zscan

参考scan

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值