当前位置: 首页 > news >正文

3.List,set 与 Zset(Redis数据类型)

3.List 列表

列表类型是⽤来存储多个有序的字符串,如下图,a、b、c、d、e 五个元素从左到右组成

了⼀个有序的列表,列表中的每个字符串称为元素(element),⼀个列表最多可以存储2^32-1个元素。在 Redis 中,可以对列表两端插⼊(push)和弹出(pop),还可以获取指定范围的元素列表、获取指定索引下标的元素等。列表是⼀种⽐较灵活的数据结构,它可以充当栈和队列的⻆⾊,在实际开发上有很多应⽤场景。

list 内部的结果(编码方式),并非是一个简单的数组,而是更接近于“双端队列”(deque)

列表两端插⼊和弹出操作

列表的获取、删除等操作

列表类型的特点:

1.列表中的元素是有序的,这意味着可以通过索引下标获取某个元素或者某个范围的元素列表,

例如要获取第 5 个元素,可以执⾏ lindex user:1:messages 4 或者倒数第 1 个元素,lindex user:1:messages -1 就可以得到元素 e。(如果把元素位置颠倒,顺序调换。此时得到的新list 和之前的list 是不等价的!!)

2.区分获取和删除的区别,lrem 1 b 是从列表中把从左数遇到的前 1 个 b 元素删除,这个操作会导致列表的⻓度从 5 变成 4;但是执⾏ lindex 4 只会获取元素,但列表⻓度是不会变化的。

3.列表中的元素是允许重复的。

命令

lpush

将⼀个或者多个元素从左侧放⼊(头插)到 list 中

LPUSH key element [element ...]时间复杂度:只插⼊⼀个元素为 O(1), 插⼊多个元素为 O(N), N 为插⼊元素个数.
返回值:插⼊后 list 的⻓度redis> LPUSH mylist "world"
(integer) 1
redis> LPUSH mylist "hello"
(integer) 2
redis> LRANGE mylist 0 -1
1) "hello"
2) "world"

lpushx

在 key 存在时,将⼀个或者多个元素从左侧放⼊(头插)到 list 中。不存在,直接返回

LPUSHX key element [element ...]时间复杂度:只插⼊⼀个元素为 O(1), 插⼊多个元素为 O(N), N 为插⼊元素个数.
返回值:插⼊后 list 的⻓度。redis> LPUSH mylist "World"
(integer) 1
redis> LPUSHX mylist "Hello"
(integer) 2
redis> LPUSHX myotherlist "Hello"
(integer) 0
redis> LRANGE mylist 0 -1
1) "Hello"
2) "World"
redis> LRANGE myotherlist 0 -1
(empty array)

rpush

将⼀个或者多个元素从右侧放⼊(尾插)到 list 中

语法:
RPUSH key element [element ...]时间复杂度:只插⼊⼀个元素为 O(1), 插⼊多个元素为 O(N), N 为插⼊元素个数.
返回值:插⼊后 list 的⻓度。redis> RPUSH mylist "world"
(integer) 1
redis> RPUSH mylist "hello"
(integer) 2
redis> LRANGE mylist 0 -1
1) "world"
2) "hello"

lrange

获取从 start 到 end 区间的所有元素,左闭右闭。

LRANGE key start stop时间复杂度:O(N)
返回值:指定区间的元素。redis> RPUSH mylist "one"
(integer) 1
redis> RPUSH mylist "two"
(integer) 2
redis> RPUSH mylist "three"
(integer) 3
redis> LRANGE mylist 0 0
1) "one"
redis> LRANGE mylist -3 2
1) "one"
2) "two"
3) "three"
redis> LRANGE mylist -100 100
1) "one"
2) "two"
3) "three"
redis> LRANGE mylist 5 10
(empty array)Redis,尽可能的获取到给定区间的元素,如果给定区间非法,比如超出下标
就会尽可能地获取对应的内容~~

lpop

从 list 左侧取出元素(即头删)

LPOP key时间复杂度:O(1)
返回值:取出的元素或者 nil。redis> RPUSH mylist "one" "two" "three" "four" "five"
(integer) 5
redis> LPOP mylist
"one"
redis> LPOP mylist
"two"
redis> LPOP mylist
"three"
redis> LRANGE mylist 0 -1
1) "four"
2) "five"

rpop

从 list 右侧取出元素(即尾删)

RPOP key时间复杂度:O(1)
返回值:取出的元素或者 nil。redis> RPUSH mylist "one" "two" "three" "four" "five"
(integer) 5
redis> RPOP mylist
"five"
redis> LRANGE mylist 0 -1
1) "one"
2) "two"
3) "three"
4) "four"

lindex

获取从左数第 index 位置的元素。

LINDEX key index时间复杂度:O(N)
返回值:取出的元素或者 nil。redis> LPUSH mylist "World"
(integer) 1
redis> LPUSH mylist "Hello"
(integer) 2
redis> LINDEX mylist 0
"Hello"
redis> LINDEX mylist -1
"World"
redis> LINDEX mylist 3
(nil)

linsert

在特定位置插⼊元素。

万一要插入的列表中,基准值存在多个怎么办?linsert 进行插入的时候,要根据基准值,找到对应的位置。从左往右找,找到第一个符合基准值的位置即可

LINSERT key <BEFORE | AFTER> pivot element时间复杂度:O(N)
返回值:插⼊后的 list ⻓度redis> RPUSH mylist "Hello"
(integer) 1
redis> RPUSH mylist "World"
(integer) 2
redis> LINSERT mylist BEFORE "World" "There"
(integer) 3
redis> LRANGE mylist 0 -1
1) "Hello"
2) "There"
3) "World"

llen

获取 list ⻓度。

LLEN key时间复杂度:O(1)
返回值:list 的⻓度。redis> LPUSH mylist "World"
(integer) 1
redis> LPUSH mylist "Hello"
(integer) 2
redis> LLEN mylist
(integer) 2

阻塞版本命令

阻塞:当前的线程,不走了,代码不继续执行了,会在满足一定的条件之后,被唤醒。

关于生产消费者模型,可见另一篇《回顾生产消费者模型》

blpop 和 brpop 是 lpop 和 rpop 的阻塞版本,和对应⾮阻塞版本的作⽤基本⼀致,除了:

1.在列表中有元素的情况下,阻塞和⾮阻塞表现是⼀致的。但如果列表中没有元素,⾮阻塞版本会理解返回 nil,但阻塞版本会根据 timeout,阻塞⼀段时间,期间 Redis 可以执⾏其他命令,但要求执⾏该命令的客⼾端会表现为阻塞状态。

2.命令中如果设置了多个键,那么会从左向右进⾏遍历键,⼀旦有⼀个键对应的列表中可以弹出元

素,命令⽴即返回。

3.如果多个客⼾端同时多⼀个键执⾏ pop,则最先执⾏命令的客⼾端会得到弹出的元素

blpop

BLPOP key [key ...] timeout时间复杂度:O(1)
返回值:取出的元素或者 nilredis> EXISTS list1 list2
(integer) 0
redis> RPUSH list1 a b c
(integer) 3
redis> BLPOP list1 list2 0
1) "list1"
2) "a"

brpop

BRPOP key [key ...] timeout时间复杂度:O(1)
返回值:取出的元素或者 nil。redis> DEL list1 list2
(integer) 0
redis> RPUSH list1 a b c
(integer) 3
redis> BRPOP list1 list2 0
1) "list1"
2) "c"

内部编码

列表类型的内部编码有两种:

1.ziplist(压缩列表):当列表的元素个数⼩于 list-max-ziplist-entries 配置(默认 512 个),同时列表中每个元素的⻓度都⼩于 list-max-ziplist-value 配置(默认 64 字节)时,Redis 会选⽤ ziplist 来作为列表的内部编码实现来减少内存消耗。

2.linkedlist(链表):当列表类型⽆法满⾜ ziplist 的条件时,Redis 会使⽤ linkedlist 作为列表的内

部实现。

使用场景

消息队列

Redis 可以使⽤ lpush + brpop 命令组合实现经典的阻塞式⽣产者-消费者模型队列,⽣产者客⼾端使⽤ lpush 从列表左侧插⼊元素,多个消费者客⼾端使⽤ brpop 命令阻塞式地从队列中"争抢" 队⾸元素。通过多个客⼾端来保证消费的负载均衡和⾼可⽤性

 

分频道的消息队列

Redis 同样使⽤ lpush + brpop 命令,但通过不同的键模拟频道的概念,不同的消费者可以通过 brpop 不同的键值,实现订阅不同频道的理念。

微博 Timeline

每个⽤⼾都有属于⾃⼰的 Timeline(微博列表),现需要分⻚展⽰⽂章列表。此时可以考虑使⽤

列表,因为列表不但是有序的,同时⽀持按照索引范围获取元素。

1.每篇微博使⽤哈希结构存储,例如微博中 3 个属性:title、timestamp、content:

hmset mblog:1 title xx timestamp 1476536196 content xxxxx
...
hmset mblog:n title xx timestamp 1476536196 content xxxxx

2.向⽤⼾ Timeline 添加微博,user:<uid>:mblogs 作为微博的键:

lpush user:1:mblogs mblog:1 mblog:3
...
lpush user:k:mblogs mblog:9

3.分⻚获取⽤⼾的 Timeline,例如获取⽤⼾ 1 的前 10 篇微博

keylist = lrange user:1:mblogs 0 9
for key in keylist {hgetall key
}

此⽅案在实际中可能存在两个问题:

1. 1 + n 问题。即如果每次分⻚获取的微博个数较多,需要执⾏多次 hgetall 操作,此时可以考虑使⽤ pipeline(流⽔线)模式批量提交命令,或者微博不采⽤哈希类型,⽽是使⽤序列化的字符串类型,使⽤ mget 获取。

2. 分裂获取⽂章时,lrange 在列表两端表现较好,获取列表中间的元素表现较差,此时可以考虑将列表做拆分。

选择列表类型时,参考:
同侧存取(lpush + lpop 或者 rpush + rpop)为栈
异侧存取(lpush + rpop 或者 rpush + lpop)为队列

 

 4. set 集合

        集合类型也是保存多个字符串类型的元素的,但和列表类型不同的是,集合中 1)元素之间是⽆序的 2)元素不允许重复。⼀个集合中最多可以存储 个元素。Redis 除了⽀持集合内的增删查改操作,同时还⽀持多个集合取交集、并集、差集,合理地使⽤好集合类型,能在实际开发中解决很多问题。(不同于C++,std::set 背后的数据结构是 红黑树)

命令:

sadd

将⼀个或者多个元素添加到 set 中。注意,重复的元素⽆法添加到 set 中。

SADD key member [member ...]时间复杂度:O(1)
返回值:本次添加成功的元素个数redis> SADD myset "Hello"
(integer) 1
redis> SADD myset "World"
(integer) 1
redis> SADD myset "World"
(integer) 0
redis> SMEMBERS myset
1) "Hello"
2) "World"

smembers

获取⼀个 set 中的所有元素,注意,元素间的顺序是⽆序的。

SMEMBERS key时间复杂度:O(N)
返回值:所有元素的列表。redis> SADD myset "Hello"
(integer) 1
redis> SADD myset "World"
(integer) 1
redis> SMEMBERS myset
1) "Hello"
2) "World"

sismember

判断⼀个元素在不在 set 中。

SISMEMBER key member时间复杂度:O(1)
返回值:1 表⽰元素在 set 中。0 表⽰元素不在 set 中或者 key 不存在。redis> SADD myset "one"
(integer) 1
redis> SISMEMBER myset "one"
(integer) 1
redis> SISMEMBER myset "two"
(integer) 0时间复杂度:O(1)
返回值:1 表⽰元素在 set 中。0 表⽰元素不在 set 中或者 key 不存在。

scard

获取⼀个 set 的基数(cardinality),即 set 中的元素个数。

SCARD key时间复杂度:O(1)
返回值:set 内的元素个数。redis> SADD myset "Hello"
(integer) 1
redis> SADD myset "World"
(integer) 1
redis> SCARD myset
(integer) 2

spop

从 set 中删除并返回⼀个或者多个元素。注意,由于 set 内的元素是⽆序的,所以取出哪个元素实际是未定义⾏为,即可以看作随机的。

SPOP key [count]时间复杂度:O(N), n 是 count
返回值:取出的元素。redis> SADD myset "one"
(integer) 1
redis> SADD myset "two"
(integer) 1
redis> SADD myset "three"
(integer) 1
redis> SPOP myset
"one"
redis> SMEMBERS myset
1) "three"
2) "two"
redis> SADD myset "four"
(integer) 1
redis> SADD myset "five"
(integer) 1
redis> SPOP myset 3
1) "three"
2) "four"
3) "two"
redis> SMEMBERS myset
1) "five"

smove

将⼀个元素从源 set 取出并放⼊⽬标 set 中

SMOVE source destination member时间复杂度:O(1)
返回值:1 表⽰移动成功,0 表⽰失败redis> SADD myset "one"
(integer) 1
redis> SADD myset "two"
(integer) 1
redis> SADD myotherset "three"
(integer) 1
redis> SMOVE myset myotherset "two"
(integer) 1
redis> SMEMBERS myset
1) "one"
redis> SMEMBERS myotherset
1) "three"
2) "two

srem

将指定的元素从 set 中删除。

SREM key member [member ...]时间复杂度:O(N), N 是要删除的元素个数.
返回值:本次操作删除的元素个数。redis> SADD myset "one"
(integer) 1
redis> SADD myset "two"
(integer) 1
redis> SADD myset "three"
(integer) 1
redis> SREM myset "one"
(integer) 1
redis> SREM myset "four"
(integer) 0
redis> SMEMBERS myset
1) "three"
2) "two"

集合间操作

集合求交集、并集、差集

sinter

获取给定 set 的交集中的元素

SINTER key [key ...]时间复杂度:O(N * M), N 是最⼩的集合元素个数. M 是最⼤的集合元素个数.
返回值:交集的元素。redis> SADD key1 "a"
(integer) 1
redis> SADD key1 "b"
(integer) 1
redis> SADD key1 "c"
(integer) 1
redis> SADD key2 "c"
(integer) 1
redis> SADD key2 "d"
(integer) 1
redis> SADD key2 "e"
(integer) 1
redis> SINTER key1 key2
1) "c"

sinterstore

获取给定 set 的交集中的元素并保存到⽬标 set 中。

SINTERSTORE destination key [key ...]时间复杂度:O(N * M), N 是最⼩的集合元素个数. M 是最⼤的集合元素个数.
返回值:交集的元素个数。redis> SADD key1 "a"
(integer) 1
redis> SADD key1 "b"
(integer) 1
redis> SADD key1 "c"
(integer) 1
redis> SADD key2 "c"
(integer) 1
redis> SADD key2 "d"
(integer) 1
redis> SADD key2 "e"
(integer) 1
redis> SINTERSTORE key key1 key2
(integer) 1
redis> SMEMBERS key
1) "c"

sunion

获取给定 set 的并集中的元素。

SUNION key [key ...]时间复杂度:O(N), N 给定的所有集合的总的元素个数.
返回值:并集的元素。redis> SADD key1 "a"
(integer) 1
redis> SADD key1 "b"
(integer) 1
redis> SADD key1 "c"
(integer) 1
redis> SADD key2 "c"
(integer) 1
redis> SADD key2 "d"
(integer) 1
redis> SADD key2 "e"
(integer) 1
redis> SUNION key1 key2
1) "a"
2) "c"
3) "e"
4) "b"
5) "d"

sunionstore

获取给定 set 的并集中的元素并保存到⽬标 set 中。

SUNIONSTORE destination key [key ...]时间复杂度:O(N), N 给定的所有集合的总的元素个数.
返回值:并集的元素个数。redis> SADD key1 "a"
(integer) 1
redis> SADD key1 "b"
(integer) 1
redis> SADD key1 "c"
(integer) 1
redis> SADD key2 "c"
(integer) 1
redis> SADD key2 "d"
(integer) 1
redis> SADD key2 "e"
(integer) 1
redis> SUNIONSTORE key key1 key2
(integer) 5
redis> SMEMBERS key
1) "a"
2) "c"
3) "e"
4) "b"
5) "d"

sdiff

获取给定 set 的差集中的元素。

SDIFF key [key ...]时间复杂度:O(N), N 给定的所有集合的总的元素个数.
返回值:差集的元素。redis> SADD key1 "a"
(integer) 1
redis> SADD key1 "b"
(integer) 1
redis> SADD key1 "c"
(integer) 1
redis> SADD key2 "c"
(integer) 1
redis> SADD key2 "d"
(integer) 1
redis> SADD key2 "e"
(integer) 1
redis> SDIFF key1 key2
1) "a"
2) "b"

sdiffstore

获取给定 set 的差集中的元素并保存到⽬标 set 中。

SDIFFSTORE destination key [key ...]时间复杂度:O(N), N 给定的所有集合的总的元素个数.
返回值:差集的元素个数redis> SADD key1 "a"
(integer) 1
redis> SADD key1 "b"
(integer) 1
redis> SADD key1 "c"
(integer) 1
redis> SADD key2 "c"
(integer) 1
redis> SADD key2 "d"
(integer) 1
redis> SADD key2 "e"
(integer) 1
redis> SDIFFSTORE key key1 key2
(integer) 2
redis> SMEMBERS key
1) "a"
2) "b

内部编码

集合类型的内部编码有两种:

1.intset(整数集合):当集合中的元素都是整数并且元素的个数⼩于 set-max-intset-entries 配置

(默认 512 个)时,Redis 会选⽤ intset 来作为集合的内部实现,从⽽减少内存的使⽤。

2.hashtable(哈希表):当集合类型⽆法满⾜ intset 的条件时,Redis 会使⽤ hashtable 作为集合

的内部实现。

 

使用场景

1.用set给用户保存标签

集合类型⽐较典型的使⽤场景是标签(tag)。例如 A ⽤⼾对娱乐、体育板块⽐较感兴趣,B ⽤⼾对历史、新闻⽐较感兴趣,这些兴趣点可以被抽象为标签。有了这些数据就可以得到喜欢同⼀个标签的⼈,以及⽤⼾的共同喜好的标签,这些数据对于增强⽤⼾体验和⽤⼾黏度都⾮常有帮助。 例如⼀个电⼦商务⽹站会对不同标签的⽤⼾做不同的产品推荐(信息茧房

2.使用set来计算用户之间的共同好友

基于“集合求交集”

3.使用set 统计 UV(去重)

 5. Zset 有序集合

它保留了集合不能有重复成员的特点,但与集合不同的是,有序集合中的每个元素都有⼀个唯⼀的浮点类型的分数(score)与之关联,着使得有序集合中的元素是可以维护有序性的,但这个有序不是⽤下标作为排序依据⽽是⽤这个分数。(set,唯一,无序)

有序集合提供了获取指定分数和元素范围查找、计算成员排名等功能,合理地利⽤有序集合,可

以帮助我们在实际开发中解决很多问题(分数不同,按照分数来排;分数相同,按照元素字典序来排列)

有序集合中的元素是不能重复的,但分数允许重复。类比于⼀次考试之后,每个人⼀定有⼀

个唯⼀的分数,但分数允许相同

普通命令

*zadd

添加或者更新指定的元素以及关联的分数到 zset 中,分数应该符合 double 类型,+inf/-inf 作为正负极限也是合法的。

ZADD 的相关选项:

• XX:仅仅⽤于更新已经存在的元素,不会添加新元素。
• NX:仅⽤于添加新元素,不会更新已经存在的元素。
• CH:默认情况下,ZADD 返回的是本次添加的元素个数,但指定这个选项之后,就会还包含本次更
新的元素的个数。
• INCR:此时命令类似 ZINCRBY 的效果,将元素的分数加上指定的分数。此时只能指定⼀个元素和
分数。

语法
ZADD key [NX | XX] [GT | LT] [CH] [INCR] score member [score member ...]时间复杂度:O(log(N))
返回值:本次添加成功的元素个数。redis> ZADD myzset 1 "one"
(integer) 1
redis> ZADD myzset 1 "uno"
(integer) 1
redis> ZADD myzset 2 "two" 3 "three"
(integer) 2
redis> ZRANGE myzset 0 -1 WITHSCORES
1) "one"
2) "1"
3) "uno"
4) "1"
5) "two"
6) "2"
7) "three"
8) "3"
redis> ZADD myzset 10 one 20 two 30 three
(integer) 0
redis> ZRANGE myzset 0 -1 WITHSCORES
1) "uno"
2) "1"
3) "one"
4) "10"
5) "two"
6) "20"
7) "three"
8) "30"
redis> ZADD myzset CH 100 one 200 two 300 three
(integer) 3
redis> ZRANGE myzset 0 -1 WITHSCORES
1) "uno"
2) "1"
3) "one"
4) "100"
5) "two"
6) "200"
7) "three"
8) "300"
redis> ZADD myzset XX 1 one 2 two 3 three 4 four 5 five
(integer) 0
redis> ZRANGE myzset 0 -1 WITHSCORES
1) "one"
2) "1"
3) "uno"
4) "1"
5) "two"
6) "2"
7) "three"
8) "3"
redis> ZADD myzset NX 100 one 200 two 300 three 400 four 500 five
(integer) 2
redis> ZRANGE myzset 0 -1 WITHSCORES1) "one"2) "1"3) "uno"4) "1"5) "two"6) "2"7) "three"8) "3"9) "four"
10) "400"
11) "five"
12) "500"
redis> ZADD myzset INCR 10 one
"11"
redis> ZRANGE myzset 0 -1 WITHSCORES1) "uno"
2) "1"3) "two"4) "2"5) "three"6) "3"7) "one"8) "11"9) "four"
10) "400"
11) "five"
12) "500"
redis> ZADD myzset -inf "negative infinity" +inf "positive infinity"
(integer) 2
redis> ZRANGE myzset 0 -1 WITHSCORES1) "negative infinity"2) "-inf"3) "uno"4) "1"5) "two"6) "2"7) "three"8) "3"9) "one"
10) "11"
11) "four"
12) "400"
13) "five"
14) "500"
15) "positive infinity"
16) "inf"

zcard

获取⼀个 zset 的基数(cardinality),即 zset 中的元素个数。

语法:
ZCARD key时间复杂度:O(1)
返回值:zset 内的元素个数。redis> ZADD myzset 1 "one"
(integer) 1
redis> ZADD myzset 2 "two"
(integer) 1
redis> ZCARD myzset
(integer) 2

*zcount(默认闭区间,排除边界值,可加上括号)

返回分数在 min 和 max 之间的元素个数,默认情况下,min 和 max 都是包含的,可以通过 ( 排除。

zcount key(95,97)格式是错的

zcount key(95(97     对的(想要闭区间可以【,或直接默认)

查询位置的过程:

zcount 在计算的时候,就是先根据得到的分数,再根据元素获取得到的排名,再把排名-1,得到了元素个数。(redis 排名是从0开始)

ZCOUNT key min max时间复杂度: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> ZCOUNT myzset -inf +inf
(integer) 3
redis> ZCOUNT myzset 1 3
(integer) 3
redis> ZCOUNT myzset (1 3
(integer) 2
redis> ZCOUNT myzset (1 (3
(integer) 1

zrange

返回指定区间⾥的元素,分数按照升序。带上 WITHSCORES 可以把分数也返回。

redis 内部存储数据时,是按照二进制的方式来存储的,要把二进制字节对回到汉字,还需要客户端来支持(redis-cli --raw)

语法:
ZRANGE key start stop [WITHSCORES]此处的 [start, stop] 为下标构成的区间. 从 0 开始, ⽀持负数.时间复杂度:O(log(N)+M)
返回值:区间内的元素列表。redis> ZADD myzset 1 "one"
(integer) 1
redis> ZADD myzset 2 "two"
(integer) 1
redis> ZADD myzset 3 "three"
(integer) 1
redis> ZRANGE myzset 0 -1 WITHSCORES
1) "one"
2) "1"
3) "two"
4) "2"
5) "three"
6) "3"
redis> ZRANGE myzset 0 -1
1) "one"
2) "two"
3) "three"
redis> ZRANGE myzset 2 3
1) "three"
redis> ZRANGE myzset -2 -1
1) "two"
2) "three"

zrevrange

返回指定区间⾥的元素,分数按照降序。带上 WITHSCORES 可以把分数也返回。

备注:这个命令可能在 6.2.0 之后废弃,并且功能合并到 ZRANGE 中。

语法:
ZREVRANGE key start stop [WITHSCORES]时间复杂度:O(log(N)+M)
返回值:区间内的元素列表。redis> ZADD myzset 1 "one"
(integer) 1
redis> ZADD myzset 2 "two"
(integer) 1
redis> ZADD myzset 3 "three"
(integer) 1
redis> ZREVRANGE myzset 0 -1 WITHSCORES
1) "three"
2) "3"
3) "two"
4) "2"
5) "one"
6) "1"
redis> ZREVRANGE myzset 0 -1
1) "three"
2) "two"
3) "one"
redis> ZREVRANGE myzset 2 3
1) "one"
redis> ZREVRANGE myzset -2 -1
1) "two"
2) "one"

zrangebyscore

返回分数在 min 和 max 之间的元素,默认情况下,min 和 max 都是包含的,可以通过。

备注:这个命令可能在 6.2.0 之后废弃,并且功能合并到 ZRANGE 中。

语法:
ZRANGEBYSCORE key min max [WITHSCORES]时间复杂度:O(log(N)+M)
返回值:区间内的元素列表。redis> ZADD myzset 1 "one"
(integer) 1
redis> ZADD myzset 2 "two"
(integer) 1
redis> ZADD myzset 3 "three"
(integer) 1
redis> ZRANGEBYSCORE myzset -inf +inf
1) "one"
2) "two"
3) "three"
redis> ZRANGEBYSCORE myzset 1 2
1) "one"
2) "two"
redis> ZRANGEBYSCORE myzset (1 2
1) "two"
redis> ZRANGEBYSCORE myzset (1 (2
(empty array)

zpopmax

删除并返回分数最⾼的 count 个元素

ZPOPMAX key [count]时间复杂度:O(log(N) * M)
返回值:分数和元素列表。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) "three“
2) "3”

bzpopmax

ZPOPMAX 的阻塞版本。

BZPOPMAX key [key ...] timeout时间复杂度:O(log(N))
返回值:元素列表。redis> DEL zset1 zset2
(integer) 0
redis> ZADD zset1 0 a 1 b 2 c
(integer) 3
redis> BZPOPMAX zset1 zset2 0
1) "zset1"
2) "c"
3) "2"

zpopmin

删除并返回分数最低的 count 个元素。

ZPOPMIN key [count]时间复杂度:O(log(N) * M)
返回值:分数和元素列表。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) "one"
2) "1"

bzpopmin

ZPOPMIN 的阻塞版本。

BZPOPMIN key [key ...] timeout时间复杂度:O(log(N))
返回值:元素列表。redis> DEL zset1 zset2
(integer) 0
redis> ZADD zset1 0 a 1 b 2 c
(integer) 3
redis> BZPOPMIN zset1 zset2 0
1) "zset1"
2) "a"
3) "0"

zrank

返回指定元素的排名,升序。

ZRANK key member时间复杂度: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> ZRANK myzset "three"
(integer) 2
redis> ZRANK myzset "four"
(nil)

zrevrank

返回指定元素的排名,降序。

ZREVRANK key member时间复杂度: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)

zscore

返回指定元素的分数。

ZSCORE key member时间复杂度:O(1)
返回值:分数。redis> ZADD myzset 1 "one"
(integer) 1
redis> ZSCORE myzset "one"
"1"

zrem

删除指定的元素。

ZREM key member [member ...]时间复杂度:O(M*log(N)) 
N 是整个有序集合中元素的个数
M 是参数中 member 的个数
返回值:本次操作删除的元素个数。redis> ZADD myzset 1 "one"
(integer) 1
redis> ZADD myzset 2 "two"
(integer) 1
redis> ZADD myzset 3 "three"
(integer) 1
redis> ZREM myzset "two"
(integer) 1
redis> ZRANGE myzset 0 -1 WITHSCORES
1) "one"
2) "1"
3) "three"
4) "3"

zremrangebyrank

按照排序,升序删除指定范围的元素,左闭右闭

ZREMRANGEBYRANK key start stop时间复杂度:O(log(N)+M)
返回值:本次操作删除的元素个数redis> ZADD myzset 1 "one"
(integer) 1
redis> ZADD myzset 2 "two"
(integer) 1
redis> ZADD myzset 3 "three"
(integer) 1
redis> ZREMRANGEBYRANK myzset 0 1
(integer) 2
redis> ZRANGE myzset 0 -1 WITHSCORES
1) "three"
2) "3"

zincrby

为指定的元素的关联分数添加指定的分数值。

不会光修改分数内容,也能同时移动元素位置。保持整个有序集合仍然是升序的

ZINCRBY key increment member时间复杂度:O(log(N))
返回值:增加后元素的分数。redis> ZADD myzset 1 "one"
(integer) 1
redis> ZADD myzset 2 "two"
(integer) 1
redis> ZINCRBY myzset 2 "one"
"3"
redis> ZRANGE myzset 0 -1 WITHSCORES
1) "two"
2) "2"
3) "one"
4) "3

集合间操作

有序集合的交集操作

zinterstore

 

求出给定有序集合中元素的交集并保存进⽬标有序集合中,在合并过程中以元素为单位进⾏合并,元素对应的分数按照不同的聚合⽅式和权重得到新的分数。
语法:
ZINTERSTORE destination numkeys key [key ...] [WEIGHTS weight[weight ...]] [AGGREGATE <SUM | MIN | MAX>]时间复杂度:O(N*K)+O(M*log(M)) N 是输⼊的有序集合中, 最⼩的有序集合的元素个数; K 是输⼊了
⼏个有序集合; M 是最终结果的有序集合的元素个数.
返回值:⽬标集合中的元素个数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//将新结果存储到 out 集合中;weights 2 3 表示给 zset1 中的元素分数乘以 2,
给 zset2 中的元素分数乘以 3,然后再计算交集时的分数
redis> zinterstore out 2 zset1 zset2 weights 2 3
(integer) 2
// withscores 附带它们的分数
redis> zrange out 0 -1 withscores
1) "one"
2) "5"
3) "two"
4) "10"

有序集合的并集操作

zunionstore

求出给定有序集合中元素的并集并保存进⽬标有序集合中,在合并过程中以元素为单位进⾏合并,元素对应的分数按照不同的聚合⽅式和权重得到新的分数。
ZUNIONSTORE destination numkeys key [key ...] [WEIGHTS weight[weight ...]] [AGGREGATE <SUM | MIN | MAX>]时间复杂度:O(N)+O(M*log(M)) N 是输⼊的有序集合总的元素个数; M 是最终结果的有序集合的元素
个数.
返回值:⽬标集合中的元素个数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> ZUNIONSTORE out 2 zset1 zset2 WEIGHTS 2 3
(integer) 3
redis> ZRANGE out 0 -1 WITHSCORES
1) "one"
2) "5"
3) "three"
4) "9"
5) "two"
6) "10

内部编码

有序集合类型的内部编码有两种:
 
1.ziplist(压缩列表):当有序集合的元素个数⼩于 zset-max-ziplist-entries 配置(默认 128 个),同时每个元素的值都⼩于 zset-max-ziplist-value 配置(默认 64 字节)时,Redis 会⽤ ziplist 来作为有序集合的内部实现,ziplist 可以有效减少内存的使⽤。
 
2.skiplist(跳表):当 ziplist 条件不满⾜时,有序集合会使⽤ skiplist 作为内部实现,因为此时
ziplist 的操作效率会下降。
 
 

使用场景

  有序集合⽐较典型的使⽤场景就是排⾏榜系统。例如常⻅的⽹站上的热榜信息,榜单的维度可能
是多⽅⾯的:按照时间、按照阅读量、按照点赞量。本例中我们使⽤点赞数这个维度,维护每天的热榜:
1.添加⽤⼾赞数
例如⽤⼾ james 发布了⼀篇⽂章,并获得 3 个赞,可以使⽤有序集合的 zadd 和 zincrby 功能:
zadd user:ranking:2025-09-05 3 james.之后如果再获得赞,可以使⽤ zincrby:
zincrby user:ranking:2025-09-05 1 james2.取消⽤⼾赞数
由于各种原因(例如⽤⼾注销、⽤⼾作弊等)需要将⽤⼾删除,此时需要将⽤⼾从榜单中删除掉,可
以使⽤ zrem。例如删除成员 tom:
zrem user:ranking:2025-09-05 tom3.展⽰获取赞数最多的 10 个⽤⼾
zrevrangebyrank user:ranking:2022-03-15 0 94.展⽰⽤⼾信息以及⽤⼾分数
次功能将⽤⼾名作为键后缀,将⽤⼾信息保存在哈希类型中,⾄于⽤⼾的分数和排名可以使⽤ zscore
和 zrank 来实现。//获取哈希表(Hash)user:info:tom 中的所有字段和对应值。
hgetall user:info:tom //获取有序集合(Sorted Set)user:ranking:2022-03-15 中元素 mike 的分数(score)
zscore user:ranking:2022-03-15 mike 获取有序集合 user:ranking:2022-03-15 中元素 mike 的排名(按分数从小到大排序,排名从 0 开始)
zrank user:ranking:2022-03-15 mike

渐进式遍历(化整为零)

Redis 使⽤ scan 命令进⾏渐进式遍历键,每次只遍历 “一小部分” 键,把大的遍历操作拆分成多次小操作,避免阻塞 Redis。每次 scan 命令的时间复杂度是 O(1),但是要完整地完成所有键的遍历,需要执⾏多次 scan(keys* 有风险,容易直接炸了)
 
scan 命令渐进式遍历
1.⾸次 scan 从 0 开始.
2.当 scan 返回的下次位置为 0 时, 遍历结束
每次返回 “本次遍历的键” 和 “下次遍历的游标”,直到游标返回 0 表示遍历完成
  • 首次遍历:执行 SCAN 0 COUNT 3

    • 0:表示从 “起始游标” 开始遍历。
    • COUNT 3:表示 “期望” 每次遍历 3 个键(注意:COUNT 是 “建议数量”,实际返回数量可能不同,受 Redis 内部实现影响)。
    • 返回结果:
      • 游标 2(下次遍历要从游标 2 继续)。
      • 本次遍历到的键:"w""i""e"
  • 第二次遍历:执行 SCAN 2 COUNT 3

    • 从游标 2 继续遍历。
    • 返回结果:
      • 游标 7(下次遍历要从游标 7 继续)。
      • 本次遍历到的键:"x""j""q"

scan

以渐进式的⽅式进⾏键的遍历。
这里的渐进式遍历,在遍历过程中,不会在服务器这边存储任何的状态信息。此处的遍历是随时可以终止的~~ 不会对服务器产生任何的副作用
SCAN cursor [MATCH pattern] [COUNT count] [TYPE type]
@2限制这一次遍历能获得多少个元素,默认是10;但是注意!!此处的count 和 
mysql 的 limit 不一样(他们是精准的),此处的count只是给服务器一个建议;
写入的count 和实际返回的 key 的个数不一定是完全相同的,但是不会差很多~时间复杂度:O(1)
返回值:下⼀次 scan 的游标(cursor)以及本次得到的键。redis 127.0.0.1:6379> scan 0
1) "17"
2) 1) "key:12"2) "key:8"3) "key:4"4) "key:14"5) "key:16"6) "key:17"7) "key:15"8) "key:10"9) "key:3"10) "key:7"11) "key:1"命令:scan 0
0 是起始游标,表示从 “游标 0” 开始第一次遍历。
结果结构:
第一部分:"17" → 这是下一次遍历的游标。后续要继续遍历,需要执行 scan 17。
第二部分:是本次遍历实际获取到的键列表,比如 "key:12"、"key:8"、"key:4" 等,共 11 个键。
除了 scan 以外,Redis ⾯向哈希类型、集合类型、有序集合类型分别提供了 hscan、sscan、zscan 命令,它们的⽤法和 scan 基本类似
 

6.数据库管理

Redis 提供了⼏个⾯向 Redis 数据库的操作,分别是 dbsize、select、flushdb、flushall 命令。
 

1.切换数据库

select dbIndex
  许多关系型数据库,例如 MySQL ⽀持在⼀个实例下有多个数据库存在的,但是与关系型数据库⽤字符来区分不同数据库名不同,Redis 只是⽤数字作为多个数据库的实现。Redis 默认配置中是有 16 个数据库。select 0 操作会切换到第⼀个数据库,select 15 会切换到最后⼀个数据库。0 号数据库和 15 号数据库保存的数据是完全不冲突的,即各种有各⾃的键值对。默认情况下,我们处于数据库 0。

Redis 管理的数据库

2.清除数据库

flushdb / flushall 命令⽤于清除数据库,区别在于 flushdb 只清除当前数据库,flushall 会清楚所有数据库。
 

4.stream

Redis 中的 Stream 是 Redis 5.0 引入的一种持久化、支持多消费者的消息队列数据结构,专为处理实时数据流场景设计。它结合了传统消息队列的特性(如发布 - 订阅)和日志的持久化能力,支持消息的有序存储、重复消费、分组消费等高级功能,是 Redis 中最完善的消息队列实现。

stream类型,可以用来模拟实现事件传播机制;stream 就是一个队列(阻塞队列)redis 作为一个消息队列的重要支撑。属于是 List blpop / brpop 的升级版本。

一、核心特性

  1. 持久化:所有消息会写入 Redis 持久化文件(RDB/AOF),重启后数据不丢失,解决了 Redis 发布订阅(Pub/Sub)消息易丢失的问题。

  2. 有序性:消息按写入顺序存储,每个消息有唯一的 ID(格式为 时间戳-序列号,如 1620000000000-0),保证全局有序。

  3. 多消费者模式

    • 独立消费:多个消费者可独立读取 Stream,各自维护消费位置(类似多个独立的 “指针”)。
    • 消费组:支持将消费者分组,组内消息共享但仅被消费一次(避免重复处理),适合分布式场景下的负载均衡。
  4. 消息回溯:消费者可通过指定消息 ID 重新消费历史消息,支持 “从开头读”“从某个时间点读” 等灵活方式。

  5. 自动裁剪:支持通过配置限制 Stream 长度(如保留最近 1000 条消息),避免内存溢出。

具体操作暂不介绍

Redis Stream 是功能完备的消息队列实现,兼顾了高性能、持久化和灵活的消费模式,特别适合需要低延迟、分布式处理、消息不丢失的场景。相比传统消息队列,它的优势在于与 Redis 生态无缝集成,部署和维护成本更低,是中小规模流数据处理的理想选择。

5.Redis geospatial(用来存储坐标(经纬度))

6.Redis HyperLogLog(应用场景只有一个,估算集合中的元素)

7.Redis bitmaps(使用bit 位来表示整数)

位图本质上,还是一个集合,属于Set 类型的特化版本 --> 节省空间~ --> HyperLogLog 更节省空间啊!!既可以存储数字,也可以存储字符串~~不存储元素内容,只是计数效果 --> 但是hyperloglog 存储元素的时候,提取特征的过程是不可逆的!(信息量丢失了)-- 猪肉变成火腿,但火腿不能变成猪肉。

8.Redis bitfields

edis 的 BITFIELD 命令是一个强大的工具,用于对 Redis 字符串(String)类型中的二进制位(bit)进行细粒度操作。它允许你按 “位字段”(连续的若干位)来读取、修改字符串中的二进制数据,支持多种整数类型(如无符号 / 有符号的 8 位、16 位、32 位、64 位整数),极大扩展了 Redis 对二进制数据的处理能力。

bitfields(可以理解成一串二进制序列【字节数组】)同时可以把这个字节数组中的某几位,赋予特定的含义,并且可以进行 读取/修改/算术运算 相关操作~~

一、核心作用

Redis 字符串本质上是二进制安全的字节序列(每个字节 8 位),BITFIELD 让你可以:

 

  • 将字符串中的某一段连续位(如从第 10 位开始的 16 位)视为一个整数(如 16 位有符号整数)。
  • 对这个 “位字段” 执行读取设置自增 / 自减等操作。
  • 一次执行多个位字段操作(批量处理),减少网络交互开销。

(回顾内存对齐的规则)

  1. 基本类型对齐规则

    • 每个基本数据类型(如 int、double 等)都有一个自然对齐系数(通常等于其大小)
    • 例如:char (1 字节)、short (2 字节)、int (4 字节)、double (8 字节)
  2. 结构体成员对齐

    • 结构体中每个成员的偏移量必须是该成员对齐系数的整数倍
    • 若前一个成员结束位置不符合当前成员的对齐要求,会自动填充空白字节
  3. 结构体整体对齐

    • 结构体的总大小必须是其所有成员中最大对齐系数的整数倍
    • 若总大小不满足,会在结构体末尾填充空白字节
  4. #pragma pack 指令

    • 可以通过#pragma pack(n)手动指定对齐系数 n
    • 此时实际对齐系数为 "min (自然对齐系数,n)"
    • 使用#pragma pack()恢复默认对齐方式

 


文章转载自:

http://ZfclpJbf.nchsz.cn
http://WsBUpDw4.nchsz.cn
http://gKO4w4I8.nchsz.cn
http://D7GRJ2DW.nchsz.cn
http://YBgk53sb.nchsz.cn
http://gpg75BJo.nchsz.cn
http://7W9j3IKJ.nchsz.cn
http://N83tUnfW.nchsz.cn
http://dwJ2ZuNX.nchsz.cn
http://v2B0yzfT.nchsz.cn
http://ExndwN4d.nchsz.cn
http://dRSzcuEn.nchsz.cn
http://57LhZ4Zh.nchsz.cn
http://k2hYylST.nchsz.cn
http://y0B7Feaj.nchsz.cn
http://bbMKpSkU.nchsz.cn
http://HhdugafG.nchsz.cn
http://6ddt4P3g.nchsz.cn
http://ZBxISSMA.nchsz.cn
http://O0cO9l51.nchsz.cn
http://GIoslfoo.nchsz.cn
http://HD8zimZx.nchsz.cn
http://pRkn12os.nchsz.cn
http://MvrETk2d.nchsz.cn
http://kJJPkH1O.nchsz.cn
http://kBJBqyUF.nchsz.cn
http://5D6p6F2E.nchsz.cn
http://3nEAK0Mv.nchsz.cn
http://jvyl7SCf.nchsz.cn
http://SFHm4qrF.nchsz.cn
http://www.dtcms.com/a/378233.html

相关文章:

  • 前沿探索:RISC-V 架构 MCU 在航天级辐射环境下的可靠性测试
  • 苹果上架App软件全流程指南:iOS 应用发布步骤、App Store 上架流程、uni-app 打包上传与审核技巧详解
  • NW622NW623美光固态闪存NW624NW635
  • 38.自编码器:AI的压缩与重建艺术
  • leetcode-python-2418按身高排序
  • 【学习日记】
  • 【Android View】事件分发机制
  • 深入了解linux系统—— 线程池
  • 视频理解新纪元!VideoChat双模架构突破视频对话瓶颈,开启多模态交互智能时代
  • 【115】基于51单片机GSM防火防盗报警系统【Proteus仿真+Keil程序+报告+原理图】
  • 传统模型RNN与CNN介绍
  • 分布式专题——10.1 ShardingSphere介绍
  • 视频版权保护有哪些好用的加密方案
  • Rust 开发环境安装与 crates.io 国内源配置(Windows / macOS / Linux 全流程)
  • 前端全链路质量监控体系建设与实践分享
  • 使用python脚本储存mosquito服务器数据到sqlite
  • win10使用ssh访问vmware虚拟机
  • 高并发服务器-多路IO转接-select
  • 【WRF-VPRM 预处理器】HEG 安装(服务器)-MRT工具替代
  • 你知道服务器和电脑主机的区别吗?
  • 接力邓承浩,姜海荣能讲好深蓝汽车新故事吗?
  • 广东充电芯片助力新能源汽车车载系统升级
  • 大数据电商流量分析项目实战:Day2-1 补充Mysql和sql安装和学习
  • 【Unity UGUI 交互组件——Dropdown(TMP版本)(10)】
  • 自动化拨号爬虫体系:虚拟机集群部署与增量管理
  • 【机器人运动学】正运动学分析
  • 基于机器学习的P2P网贷平台信用违约预测模型
  • 工厂怎么认证iso14067
  • flutter项目 -- 换logo、名称 、签名、打包
  • 【Windows】VMware安装Ubuntu操作系统