Redis 数据结构
Redis 的核心优势之一就是丰富且高效的数据结构(数据存储类型)。相比传统数据库只存表结构,Redis 更像是一个“数据结构服务器”。
Redis 目前主流有 7 大核心数据类型:
|
类型 |
名称 |
说明 |
|
string |
字符串 |
最基础类型(万能) |
|
list |
列表 |
有序、可重复 |
|
set |
集合 |
无序、不可重复 |
|
zset |
有序集合 |
有序 + 分数 |
|
hash |
哈希 |
类似对象 |
|
bitmap |
位图 |
位级别操作 |
|
hyperloglog |
基数统计 |
去重计数(近似) |
|
stream |
流 |
消息队列(高级) |
一,string 字符串数据类型
string 字符串数据类型可以通过 SET 命令来进行设置存储,SET 的命令如下:
SET key value [NX | XX | IFEQ ifeq-value | IFNE ifne-value |
IFDEQ ifdeq-digest | IFDNE ifdne-digest] [GET] [EX seconds |
PX milliseconds | EXAT unix-time-seconds |
PXAT unix-time-milliseconds | KEEPTTL]
# 时间复杂度
后面的参数小编一个个的来解析与讲解
1.1 SET 简单使用
【redis-cli】SET key value 直接对指定的key赋值
127.0.0.1:6379> SET username toast
OK
127.0.0.1:6379> GET username
"toast"
SET key value key如果存在就是直接覆盖旧值,key 如果不存在则新建key在赋值
【redis-cli】SET key value GET 在 SET 命令赋值的基础上获取被覆盖的旧值
127.0.0.1:6379> SET age 24 GET
(nil)
127.0.0.1:6379> SET age 27 GET
"24"
127.0.0.1:6379>
1.2 MSET 设置多个key-value
127.0.0.1:6379> MSET toast-java java toast-python Python toast-go Golang
OK
127.0.0.1:6379> GET toast-java
"java"
1.3 SET 过期时间控制
SET 秒级过期时间 SET key value EX seconds
SET key value EX 60 # 表示该值 60 秒过期失效
SET 毫秒级过期时间 SET key value PX milliseconds
SET key value PX 1000 # 表示该值 1000 毫秒过期
如果是直接设置的话,是没有过期时间的
SET key value
像这种直接设置的话,没有过期时间,后期需要引入过期时间的话。就直接在重新执行一次追加有过期参数的命令即可。
如果你想在改值的时候,同时还需要保留过期时间,可以使用另外一个命令: KEEPTTL
从 命令名称 KEEPTTL 也可以看出是用于保留当前的过期时间的。
案例如下:
127.0.0.1:6379> SET user:100 hello EX 60 # 设置一个60秒过期的值
OK
127.0.0.1:6379> TTL user:100 # 查看过期时间,还有 52 秒
(integer) 52
127.0.0.1:6379> SET user:100 world KEEPTTL # 更新值并保留当前过期时间
OK
127.0.0.1:6379> TTL user:100 # 查看过期时间,还有 41 秒
(integer) 41
127.0.0.1:6379> SET user:100 world2 # 直接更新,覆盖过期时间,此时永久有效
OK
127.0.0.1:6379> TTL user:100 # 查看过期时间, -1 表示永久有效
(integer) -1
127.0.0.1:6379> SET user:100 hello EX 60 # 重新设置
1.4 SET 时间戳过期时间控制
EXAT
SET key value EXAT 1717320000
👉 表示:在 某个时间点(秒级时间戳)过期
PXAT
SET key value PXAT 1717320000000
👉 毫秒级时间戳
👉 使用场景:
- 定时任务
- 活动结束时间
- Token 到期(统一时间)
1.5 SET 条件判断写入
✔ 只在 key 不存在时写入(分布式锁核心) NX = Not eXists
SET key value NX
👉 等价于:
SETNX key value # 这个是老版的命令
只在 key 存在时写入 👉 用于更新操作 XX = eXists ×2(双 X 强调 Exist)
SET key value XX
SET NX 做锁:标准分布式锁
SET lock:order:1001 1 NX EX 30
逻辑:lock:order:1001 不存在 → 加锁成功;已存在 → 抢锁失败。
场景:全局互斥锁(下单、任务执行、接口防重、资源独占)。
特点:一把锁 = 一个独立 String Key。
1.6 SET 提供的乐观锁
IFEQ ( If Equal )
SET key newValue IFEQ oldValue
含义:只有当当前值 == oldValue,才更新 本质就是 CAS(Compare And Set)
SET counter 10
SET counter 20 IFEQ 10 # 成功
SET counter 30 IFEQ 10 # 失败
IFNE(If Not Equal)
SET key value IFNE someValue
👉 含义: 当前值 ≠ 指定值 才更新
IFDEQ / IFDNE(更底层:基于 digest)
👉 这个是更底层版本,用的是 value 的 hash/digest 比较
IFDEQ # digest equal
IFDNE # digest not equal
用途
避免大 value 比较开销 ,
内部优化 / 高性能场景
1.7 组合使用
一个场景案例:key 不存在才设置 30 秒自动过期 这个也是分布式锁标准写法
SET lock_key uuid NX EX 30
1.8 SET 的特征
SET 命令是有原子性特征的,要么成功,要么失败,不会出现并发问题
SET 命令是有写入覆盖的特点
SET key v1
SET key v2
# 最终
key = v2
SET 命令,如果 key 不存在则自动创建
1.9 总结
原子操作:整个命令要么全部执行成功,要么全部不执行,无中间状态,并发安全
覆盖默认行为:不指定任何参数时,SET key value 会直接覆盖已有 key
功能大一统:替代了老版 SETNX/SETEX/PSETEX 等命令,一条命令搞定所有场景
支持 CAS 乐观锁:Redis 6.2+ 新增条件判断,实现值比对 + 赋值原子操作
参数分类:
【过期控制参数】:EX, PX/EXAT, PXAT, KEEPTTL
【值条件】: NX, XX
【CAS】: IFEQ, IFNE, IFDEQ, IFDNE
IFEQ, IFNE, IFDEQ, IFDNE 是 Redis 8.4 新版本才出的原子CAS参数。
二,KEYS & TYPE 命令
2.1 KEYS
|
命令 |
KEYS pattern |
时间复杂度 |
O(N) |
|
描述 |
处理大型数据库时使用该命令可能会严重影响性能 |
||
pattern 支持的匹配模式:
? 单个字符匹配: h?llo 与 hello 、 hallo 和 hxllo 相匹配。
* 任意字符匹配: h*llo 与 hllo 以及 heeeello 相匹配。
[字符] 指定字符匹配: h[ae]llo 与 hello 和 hallo, 相匹配,但不与 hillo 相匹配。
[^字符] 排除字符匹配: h[^e]llo 与 hallo 、 hbllo 等相匹配,但不与 hello 相匹配。
[-] 字符范围匹配: h[a-b]llo 与 hallo 以及 hbllo 相匹配。
在实际开发之中,一般为了性能 keys 命令公司一般都是禁用,因为 Redis 往往会保存海量的数据,那么海量的数据直接查询,会严重影响到 Redis 性能。
127.0.0.1:6379> MSET toast-java java toast-python Python toast-go Golang
OK
127.0.0.1:6379> KEYS toast-java
1) "toast-java"
KEYS 也支持模糊查询,可以在内容的前置或后置追加 * 号表示模糊查询
127.0.0.1:6379> KEYS toast*
1) "toast-go"
2) "toast-java"
3) "toast-python"
2.2 TYPE
|
命令 |
TYPE key |
时间复杂度 |
O(1) |
|
描述 |
返回存储在 |
||
TYPE 命令是用于查询 key的属性类信息
127.0.0.1:6379> KEYS *
1) "addr"
2) "toast-go"
3) "age"
4) "member:toast"
5) "username"
6) "usernmae"
7) "toast-java"
8) "toast-python"
127.0.0.1:6379> TYPE member:toast
hash
127.0.0.1:6379> TYPE age
string
127.0.0.1:6379> TYPE addr
string
三,Hash 数据
如果说现在要通过 Redis 描述一个完整的对象信息(一个对象会有多个属性的内容),按照传统的做法此时需要创建有多个数据KEY。如下所示:
SET member:toast:name zhangsan
SET member:toast:age 16
SET member:toast:job Teacher
如果此时需要保存不用用户的信息,只需要修改用户名即可,但是随之而来新的问题产生了,一个完整的对象信息要通过三个数据项来进行描述,那么一旦要存放有 100 W 条的数据,那么就需要提供有 300W 个数据项,每一次数据获取的时候都需要进行哈希的定位,所以最佳的做法并不是进行拆分,而是要将这多个不同的数据项捆绑在一个 key 上,所以才有了哈希结构。
3.1 HSET 命令
|
命令 |
HSET key field value [field value ...] |
时间复杂度 |
O(1) |
|
描述 |
将指定的字段值设置为存储在 |
||
Redis 里面的 Hash 数据类型,可以理解成 Redis 里面的 JSON 数据结构。可以存储一个对象。案例如下:
HSET member:toast name zhangsan age 16 job Teacher
这样一个key 存储了一个对象,而不是三个key存储一个对象,这样节省存储空间
3.2 HGET 命令
|
命令 |
HGET key field |
时间复杂度 |
O(1) |
|
描述 |
返回存储在 |
||
HGET 就是用于获取 HSET 设置的属性,命令如下:
127.0.0.1:6379> HSET member:toast name zhangsan age 16 job Teacher
(integer) 3
127.0.0.1:6379> HGET member:toast name
"zhangsan"
127.0.0.1:6379>
3.3 HGETALL 获取全部字段命令
|
命令 |
HGETALL key |
时间复杂度 |
O(N) |
|
描述 |
返回存储在 |
||
HGETALL 用于获取全部的 HGETALL 命令
127.0.0.1:6379> HGETALL member:toast
1) "name"
2) "zhangsan"
3) "age"
4) "16"
5) "job"
6) "Teacher"
127.0.0.1:6379>
3.4 HMGET → 批量取多个字段
|
命令 |
HGETALL key |
时间复杂度 |
O(N) |
|
描述 |
返回存储在 |
||
127.0.0.1:6379> HMGET member:toast name age
1) "zhangsan"
2) "16"
127.0.0.1:6379>
3.5 HKEYS 获取所有 Hash 属性的 key 列表
|
命令 |
HKEYS key |
时间复杂度 |
O(N) |
|
描述 |
返回存储在 |
||
127.0.0.1:6379> HSET member:toast name one age 23
(integer) 0
127.0.0.1:6379> HSET member:toast name two age 24
(integer) 0
127.0.0.1:6379> HKEYS member:toast
1) "name"
2) "age"
3.6 HLEN 获取指定 key 保存的对象个数
|
命令 |
HLEN key |
时间复杂度 |
O(N) |
|
描述 |
返回存储在 |
||
127.0.0.1:6379> HSET member:toast name one age 23
(integer) 0
127.0.0.1:6379> HSET member:toast name two age 24
(integer) 0
127.0.0.1:6379> HLEN member:toast
(integer) 2
3.7 HDEL → 删除某个字段
|
命令 |
HDEL key field [field ...] |
时间复杂度 |
O(N) |
|
描述 |
从存储在 |
||
127.0.0.1:6379> HDEL member:toast job # 删除 job 字段属性和值
(integer) 1
127.0.0.1:6379> HGETALL member:toast
1) "name"
2) "zhangsan"
3) "age"
4) "16"
127.0.0.1:6379>
3.8 HVALS 获取指定key 的对象值
|
命令 |
HVALS key |
时间复杂度 |
O(N) |
|
描述 |
返回存储在 |
||
127.0.0.1:6379> HSET member:toast name one age 23
(integer) 0
127.0.0.1:6379> HSET member:toast name two age 24
(integer) 0
127.0.0.1:6379> HVALS member:toast
1) "two"
2) "24"
3.9 HEXISTS → 判断指定key的属性字段是否存在(1 - 存在 | 0 - 不存在)
|
命令 |
HEXISTS key field |
时间复杂度 |
O(1) |
|
描述 |
如果 |
||
127.0.0.1:6379> HSET member:toast name one age 23
(integer) 0
127.0.0.1:6379> HSET member:toast name two age 24
(integer) 0
127.0.0.1:6379> HEXISTS member:toast name
(integer) 1
127.0.0.1:6379> HEXISTS member:toast addr
(integer) 0
四,数字操作
在实际的开发之中一般会出现一种场景。在数据库之中有一个数字,而这个数字在高并发访问下的数据更新问题(自增操作);
redis 在设计的时候考虑到此类的需要,提供了数据的修改支持,可以直接通过命令实现数据修改操作。
4.1 INCR key
|
命令 |
INCR key |
时间复杂度 |
O(1) |
|
描述 |
将存储在 |
||
对指定key的数据进行自增,如果数就不存在则新建
127.0.0.1:6379> INCR visits
(integer) 1
127.0.0.1:6379> INCR visits
(integer) 2
127.0.0.1:6379> INCR visits
(integer) 3
127.0.0.1:6379> INCR visits
(integer) 4
127.0.0.1:6379> INCR visits
(integer) 5
127.0.0.1:6379> GET visits
"5"
4.2 DECR key
|
命令 |
DECR key |
时间复杂度 |
O(1) |
|
描述 |
将存储在 |
||
对指定key的数据进行自减,如果数据不存在则新建
# 通过 TYPE 命令得知,datas key 不存在
127.0.0.1:6379> TYPE datas
none
# key 不存在的时候,会先设置为 0 ,在减一
127.0.0.1:6379> DECR datas
(integer) -1
127.0.0.1:6379> DECR datas
(integer) -2
4.3 INCRBY key 数值
|
命令 |
INCRBY key increment |
时间复杂度 |
O(1) |
|
描述 |
将存储在 |
||
对指定key的数据进行指定步长数值追加,如果key不存在则新建
127.0.0.1:6379> INCRBY total 110
(integer) 110
127.0.0.1:6379> INCRBY total 110
(integer) 220
4.4 DECRBY key 数值
|
命令 |
DECRBY key decrement |
时间复杂度 |
O(1) |
|
描述 |
将存储在 |
||
对指定key的数据进行指定步长数值减少,如果key不存在则新增
# 通过 TYPE 命令得知,total key 不存在
127.0.0.1:6379> TYPE total
none
# key 不存在的时候,会先设置为 0 ,在减指定步长 100
127.0.0.1:6379> DECRBY total 100
(integer) -100
127.0.0.1:6379> DECRBY total 1
(integer) -101
4.5 INCRBYFLOAT 浮点数自增
|
命令 |
INCRBYFLOAT key increment |
时间复杂度 |
O(1) |
|
描述 |
将存储在 |
||
127.0.0.1:6379> INCRBYFLOAT number 100.01
"100.01"
127.0.0.1:6379> INCRBYFLOAT number 1.01
"101.02"
127.0.0.1:6379> INCRBYFLOAT number 0.21
"101.23"
127.0.0.1:6379> INCRBYFLOAT number -0.51
"100.72"
4.6 Hash 数据类型的自增自减
Redis 对 Hash 数据类型专门用于数字自增,自减的命令只有 2 个 ,一个是用于 整数形 的自增和自减,一个用于 浮点数 的自增和自减
HINCRBY —— 整数 自增 / 自减
127.0.0.1:6379> HINCRBY product:100 stock 5 # 自增 +5
(integer) 5
127.0.0.1:6379> HINCRBY product:100 stock -3 # 自减 -3 (写成负数就是自减)
(integer) 2
HINCRBYFLOAT —— 整数 自增 / 自减
127.0.0.1:6379> HINCRBYFLOAT order:100 price 1.5
"1.5"
127.0.0.1:6379> HINCRBYFLOAT order:100 price -0.8
"0.7"
HINCRBY / HINCRBYFLOAT 同样是原子操作,并发安全 。key不存在时从 0 开始记录。
同理 INCR, INCRBY, DECR, DECRBY 也是具有原子操作,并发按照。 不管多少线程、多少服务、多少并发同时执行,结果永远正确,不会出现数值错乱。
五,List 队列
Redis 里面的 List 数据结构,就是一个双向队列,并且支持高并发。对新增的元素是有序的,并且可以新增重复的元素数据。所以本质上List是一个双向,有序,可重复的字符串列队。
有序性:有序的体现就是依据插入的顺序进行排队
重复性:可以插入重复的字符串元素
双向性:可左进左出,左进右出,右进右出,右进左出。可以两头操作:左边 LPUSH、右边 RPUSH
常用作:消息队列、任务队列、排行榜、历史记录
Redis 的 List 队列是一个高性能的队列, 可以左进右出/右进左出(队列)、左进左出/右进右出(栈)
5.1 左进左出 LPUSH + LPOP → 栈 (先进后出 LIFO)
|
命令 |
LPUSH key element [element ...] |
|
描述 |
往 |
|
命令 |
LPOP key [count] |
|
描述 |
移除并返回存储在 默认情况下,该命令会从列表的左边开头选取一个元素。如果提供了可选的 |
127.0.0.1:6379> LPUSH stack left-A # 左进 left-A
(integer) 1
127.0.0.1:6379> LPUSH stack left-B # 左进 left-B
(integer) 2
127.0.0.1:6379> LPUSH stack left-C # 左进 left-C
(integer) 3
# 现在队列: 往左边逐个追加元素 ---> left-C left-B left-A
127.0.0.1:6379> LPOP stack # 左弹 left-C
"left-C"
127.0.0.1:6379> LPOP stack # 左弹 left-B
"left-B"
127.0.0.1:6379> LPOP stack # 左弹 left-A
"left-A"
空队列:[ <=====> ]
LPUSH A → [A <=====> ]
LPUSH B → [B A <=====> ]
LPUSH C → [C B A <=====> ]
LPOP → 左端取出C → [B A <=====> ]
LPOP → 左端取出B → [A <=====> ]
LPOP → 左端取出A → [ <=====> ]
5.2 右进右出 RPUSH + RPOP → 栈 (先进后出 LIFO)
|
命令 |
RPUSH key element [element ...] |
|
描述 |
往 |
|
命令 |
RPOP key [count] |
|
描述 |
移除并返回存储在 默认情况下,该命令会从列表的(右边)最末尾的元素。如果提供了可选的 |
127.0.0.1:6379> RPUSH stack right-A
(integer) 1
127.0.0.1:6379> RPUSH stack right-B
(integer) 2
127.0.0.1:6379> RPUSH stack right-C
(integer) 3
# 现在队列: left-A left-B left-C <--- 往右边逐个追加元素
127.0.0.1:6379> RPOP stack # 往右边弹出
"right-C"
127.0.0.1:6379> RPOP stack
"right-B"
127.0.0.1:6379> RPOP stack
"right-A"
空队列:[ <=====> ]
RPUSH A → [ <=====> A]
RPUSH B → [ <=====> A B]
RPUSH C → [ <=====> A B C]
RPOP → 右端取出C → [ <=====> A B]
RPOP → 右端取出B → [ <=====> A]
RPOP → 右端取出A → [ <=====> ]
5.3 左进右出 LPUSH + RPOP → 标准队列 (先进先出 FIFO)
127.0.0.1:6379> LPUSH queue A
(integer) 1
127.0.0.1:6379> LPUSH queue B
(integer) 2
127.0.0.1:6379> LPUSH queue C
(integer) 3
# 现在队列: 往左边逐个追加元素 ---> C B A
# 往右边弹出,顺序就是:A, B, C
127.0.0.1:6379> RPOP queue
"A"
127.0.0.1:6379> RPOP queue
"B"
127.0.0.1:6379> RPOP queue
"C"
空队列:[ <=====> ]
LPUSH A → [A <=====> ]
LPUSH B → [B A <=====> ]
LPUSH C → [C B A <=====> ]
RPOP → 右端取出A → [C B <=====> ]
RPOP → 右端取出B → [C <=====> ]
RPOP → 右端取出C → [ <=====> ]
5.4 右进左出|RPUSH + LPOP → 标准队列 (先进先出 FIFO)
127.0.0.1:6379> RPUSH msg_queue A
(integer) 1
127.0.0.1:6379> RPUSH msg_queue B
(integer) 2
127.0.0.1:6379> RPUSH msg_queue C
(integer) 3
# 现在队列: A B C <--- 往右边逐个追加元素
# 往左边弹出,顺序如下: A, B, C
127.0.0.1:6379> LPOP msg_queue
"A"
127.0.0.1:6379> LPOP msg_queue
"B"
127.0.0.1:6379> LPOP msg_queue
"C"
示意图
空队列:[ <=====> ]
RPUSH A → [ <=====> A]
RPUSH B → [ <=====> A B]
RPUSH C → [ <=====> A B C]
LPOP → 左端取出A → [ <=====> B C]
LPOP → 左端取出B → [ <=====> C]
LPOP → 左端取出C → [ <=====> ]
5.5 LRANGE -- 查看 List 队列
|
命令 |
LRANGE key start stop |
时间复杂度 |
O(S+N),S 起始偏移量到HEAD 的距离 |
|
描述 |
返回存储在 |
||
LRANGE 是 Redis List 列表专用的查询命令,只查看、不删除、不修改队列数据,专门用来打印 / 查看 List 里的元素。
127.0.0.1:6379> RPUSH mylist "one"
(integer) 1
127.0.0.1:6379> RPUSH mylist "two"
(integer) 2
127.0.0.1:6379> RPUSH mylist "three"
(integer) 3
# 查看队列里 所有元素(工作99%用这个)
127.0.0.1:6379> LRANGE mylist 0 -1
1) "one"
2) "two"
3) "three"
LRANGE 各种查询
LRANGE mylist 0 2 # 取前3个:A B C
LRANGE mylist 0 -1 # 取全部:A B C D E
LRANGE mylist -2 -1 # 取最后2个:D E
LRANGE mylist 1 3 # 取第2~第4个:B C D
5.6 真实场景案例: 异步短信发送队列(生产环境经典 List 使用)
场景说明
业务下单 / 注册后不阻塞主线程,手机号丢入 Redis List 队列;独立消费者进程阻塞轮询发送短信。 采用:RPUSH (右端生产) + BLPOP (左端阻塞消费) = 右进左出 FIFO
1. 生产者(业务代码写入)
# 用户注册,写入待发短信
RPUSH sms_queue "13800001111|您已注册成功"
RPUSH sms_queue "13900002222|订单已发货"
RPUSH sms_queue "13700003333|优惠券到账"
2. 消费者(后台守护进程阻塞消费,不会空轮询耗 CPU)
# 阻塞等待,超时30s,无消息阻塞休眠
BLPOP sms_queue 30
业务逻辑
取出手机号 + 文案 → 调用第三方短信接口发送 → 发送失败可重新 RPUSH 回队列重试。
补充:整队列过期管控
EXPIRE sms_queue 86400 # 队列24h过期,防止堆积脏数据
六,Set 数据类型
Redis Set(集合) 是一个无序、不可重复的字符串集合,底层通过哈希表实现,增删改查、判断成员是否存在的时间复杂度都是 O (1),非常高效。
Set 数据类型的特点如下
无序性:元素没有固定顺序,每次获取顺序可能不同
唯一性:自动去重,同一个值只能存一次
元素类型:只能存字符串,不能嵌套其他数据结构
集合操作:支持交集、并集、差集等高级运算(核心优势)
Set 集合的核心优势是处理交集,并集,差集,也就是集合与集合之间的运算。
6.1 SADD
|
命令 |
SADD key member [member ...] |
时间复杂度 |
O(N) N 表示元素个数 |
|
描述 |
将指定的成员添加到存储在 如果这些成员已经属于该集合,那么它们将被忽略。 如果 |
||
127.0.0.1:6379> SADD skills search-tools map-index-tools openclaw-tools
(integer) 3
127.0.0.1:6379> SADD skills search-tools openclaw-tools
(integer) 0
返回的数字表示新增到集合 skills 的个数,如果是有重复将忽略不添加。
6.2 SMEMBERS
|
命令 |
SMEMBERS key |
时间复杂度 |
O(N),其中 N 表示集合的基数。 |
|
描述 |
获取指定 key 集合中的全部数据 |
||
127.0.0.1:6379> SMEMBERS skills
1) "search-tools"
2) "map-index-tools"
3) "openclaw-tools"
6.3 SREM
|
命令 |
SREM key member [member ...] |
时间复杂度 |
O(N),N 是需要移除的成员数量 |
|
描述 |
从存储在 |
||
127.0.0.1:6379> SREM skills openclaw-tools hello-tools
(integer) 1
127.0.0.1:6379> SMEMBERS skills
1) "search-tools"
2) "map-index-tools"
场景案例-文章标签标案
# 给文章1001加标签
127.0.0.1:6379> SADD article:1001:tags Redis 后端 缓存 高并发
(integer) 4
# 给文章1002加标签
127.0.0.1:6379> SADD article:1002:tags MySQL 数据库 后端
(integer) 3
# 查看文章1001标签
127.0.0.1:6379> SMEMBERS article:1001:tags
Redis
后端
缓存
高并发
# 统计文章标签个数
127.0.0.1:6379> SCARD article:1001:tags
4
# 删除文章标签
127.0.0.1:6379> SREM article:1001:tags 高并发
6.4 SPOP
|
命令 |
SPOP key [count] |
时间复杂度 |
O(N),N 是需要弹出的成员数量 |
|
描述 |
默认情况下,该命令会从集合中取出一个成员。如果提供了可选的 |
||
127.0.0.1:6379> SPOP skills
"search-tools"
127.0.0.1:6379> SPOP skills 10 # 超过集合里的元素时,会全部弹出
1) "map-index-tools"
场景案例-抽奖活动:随机不重复中奖(SPOP)
# 添加抽奖名单
127.0.0.1:6379> SADD lottery:2026 zhangsan lisi wangwu zhaoliu zhouqi
(integer) 5
# 随机抽 1 人
127.0.0.1:6379> SPOP lottery:2026
"zhangsan"
# 随机抽 3 人
127.0.0.1:6379> SPOP lottery:2026 3
1) "lisi"
2) "wangwu"
3) "zhaoliu"
6.5 SDIFF 差集
|
命令 |
SDIFF key [key ...] |
时间复杂度 |
O(N),其中 N 表示所有给定集合中的元素总数 |
|
描述 |
返回从第一个集合与所有后续集合的差集所得到的集合中的元素。 |
||
127.0.0.1:6379> SADD S1 tools-A tools-B tools-C tools-D
(integer) 4
127.0.0.1:6379> SADD S2 tools-B tools-C tools-D
(integer) 3
127.0.0.1:6379> SADD S3 tools-C tools-D
(integer) 2
127.0.0.1:6379> SDIFF S1 S3 # S1 与 S3 的差集
1) "tools-B"
2) "tools-A"
127.0.0.1:6379> SDIFF S1 S2 # S1 与 S2 的差集
1) "tools-A"
127.0.0.1:6379> SDIFF S1 S2 S3 # S1 S2 S3 共同的差集
1) "tools-A"
6.6 SDIFFTORE 差集并保存
|
命令 |
SDIFFSTORE destination key1 key2 [key3 ...] |
时间复杂度 |
O(N),其中 N 表示所有给定集合中的元素总数 |
|
描述 |
Set Difference Store → 差集 + 存储 计算 |
||
差集规则:只保留【第一个集合有】、【其他所有集合都没有】的元素
结果 = key1 - (key2 ∪ key3 ∪ key4 ...)
127.0.0.1:6379> SADD set1 a b c d
(integer) 4
127.0.0.1:6379> SADD set2 b c
(integer) 2
127.0.0.1:6379> SADD set3 d
(integer) 1
127.0.0.1:6379> SDIFFSTORE result set1 set2 set3
(integer) 1
127.0.0.1:6379> SMEMBERS result
1) "a"
# set1 有:a b c d 减去 set2(b c) 再减去 set3(d) 最后剩下:a
与 SDIFF 区别
|
命令 |
作用 |
是否保存结果 |
|
SDIFF |
计算差集,只返回不保存 |
❌ 不保存 |
|
SDIFFSTORE |
计算差集,并保存到新 key |
✅ 保存 |
最经典业务场景(必看!)
场景 1:推荐 “可能认识的人”
my_friends:我的好友your_friends:你的好友- 我和你共同好友:
SINTER - 我有、你没有的好友(推荐给你):
SDIFFSTORE
# 我的关注
127.0.0.1:6379> SADD my:follow 张三 李四
2
# 朋友的关注
127.0.0.1:6379> SADD friend:follow 张三 李四 王五 赵六
4
# 差集: 好友关注 - 我关注 = 推荐我关注
127.0.0.1:6379> SDIFF friend:follow my:follow
赵六
王五
# 保存推荐结果
127.0.0.1:6379> SDIFFSTORE recommend:follow friend:follow my:follow
2
场景 2:未读消息
all_msg:所有消息read_msg:已读消息- 未读消息 = 全部 - 已读
SDIFFSTORE unread_msg all_msg read_msg
场景 3:黑名单过滤
all_users:所有用户black_users:黑名单- 可展示用户 = 全部 - 黑名单
SDIFFSTORE show_users all_users black_users
6.7 SINTER & SINTERSTORE 交集命令
|
命令 |
SINTER key [key ...] |
时间复杂度 |
最坏情况下的时间复杂度为 O(N*M),其中 N 表示最小集合的基数,M 则表示集合的数量 |
|
描述 |
Set Intersect → 求多个集合的 交集,返回【所有集合里 都同时存在】的元素 |
||
127.0.0.1:6379> SADD set1 a b c d
(integer) 4
127.0.0.1:6379> SADD set2 b c d e
(integer) 4
127.0.0.1:6379> SADD set3 c d f g
(integer) 4
127.0.0.1:6379> SINTER set1 set2 set3 # 因为只有 c、d 是三个集合都有的。
1) "c"
2) "d"
|
命令 |
作用 |
是否保存 |
|
SINTER |
求交集,直接返回结果 |
❌ 不保存 |
|
SINTERSTORE |
求交集,并保存到新 key |
✅ 保存 |
# 只看结果
127.0.0.1:6379> SINTER set1 set2 set3
1) "c"
2) "d"
# 把结果保存到新集合 resultX
127.0.0.1:6379> SINTERSTORE resultX set1 set2
(integer) 3
127.0.0.1:6379> SMEMBERS resultX
1) "b"
2) "c"
3) "d"
场景案例-共同好友(最经典)
# 用户A关注的人
127.0.0.1:6379> SADD user:follow:A 张三 李四 王五 赵六
4
# 用户B关注的人
127.0.0.1:6379> SADD user:follow:B 李四 王五 钱七 孙八
4
# A和B共同关注的人(交集)
127.0.0.1:6379> SINTER user:follow:A user:follow:B
李四
王五
# 把结果保存到新key(方便后续使用)
127.0.0.1:6379> SINTERSTORE common:follow user:follow:A user:follow:B
2
结果:李四、王五
6.8 SUNION & SUNIONSTORE 并集
|
命令 |
SUNION key [key ...] |
时间复杂度 |
O(N),其中 N 表示所有给定集合中的元素总数。 |
|
描述 |
Set Union → 求多个集合的 并集。把所有集合的元素合并在一起,自动去重,返回最终结果 |
||
|
命令 |
SUNIONSTORE destination key [key ...] |
时间复杂度 |
O(N),其中 N 表示所有给定集合中的元素总数。 |
|
描述 |
Set Union → 求多个集合的 并集。把所有集合的元素合并在一起,自动去重,返回最终结果放到 destination |
||
|
命令 |
作用 |
是否保存结果 |
|
SUNION |
求并集,直接返回 |
❌ 不保存 |
|
SUNIONSTORE |
求并集,保存到新 key |
✅ 保存 |
并集案例如下
127.0.0.1:6379> SADD set a b c
(integer) 3
127.0.0.1:6379> SADD set2 c d e
(integer) 3
127.0.0.1:6379> SADD set3 e f g
(integer) 3
# 求并集
127.0.0.1:6379> SUNION set1 set2 set3
1) "f"
2) "d"
3) "e"
4) "g"
5) "c"
127.0.0.1:6379>
所有元素合在一起,重复的 c、e 只保留一个
# 求并集,保存到新 resultX
127.0.0.1:6379> SUNIONSTORE resultX set1 set2 set3
(integer) 5
127.0.0.1:6379> SMEMBERS resultX
1) "c"
2) "d"
3) "e"
4) "f"
5) "g"
当前所有的命令都是属于 Set 数据类型的操作命令,所以新的集合 resultX 等等也都是属于 Set 数据类型,因此该数据类型是:无序,不重复的数据集合。
场景 1:合并所有好友 / 粉丝列表
- 用户 A 的好友
- 用户 B 的好友全部好友汇总 = 并集
redis
SUNION user:A:friends user:B:friends
场景 2:多标签文章查询(满足任意一个)
- 标签 1:Redis
- 标签 2:MySQL
- 标签 3:Java
只要带其中任意一个标签的文章 = 并集
场景 3:全网热搜 / 内容聚合
把多个榜单的内容合并,自动去重,展示总榜单。
三个集合命令 终极对比(必背!)
|
命令 |
含义 |
口诀 |
|
SINTER |
交集 |
大家都有的 |
|
SDIFF |
差集 |
我有你没有的 |
|
SUNION |
并集 |
全部合在一起 |
6.9 SMOVE 跨集合移动元素
|
命令 |
SMOVE source destination member |
时间复杂度 |
O(1) |
|
描述 |
SMOVE 源集合 目标集合 元素 一句话:原子操作,把元素从源集合删掉,添加到目标集合 |
||
示例
127.0.0.1:6379> SADD src a b c
(integer) 3
127.0.0.1:6379> SADD dest c d
(integer) 2
# # 把b从src移到dest
127.0.0.1:6379> SMOVE src dest b
(integer) 1
127.0.0.1:6379> SMEMBERS dest
1) "c"
2) "d"
3) "b"
127.0.0.1:6379> SMEMBERS src
1) "a"
2) "c"
业务场景
- 用户:待审核→已通过分组迁移;
- 商品:临时库存→正式上架分组挪动;
- 黑名单:临时拉黑→永久拉黑迁移。
6.10 SCARD
|
命令 |
SCARD key |
时间复杂度 |
O(1) |
|
描述 |
全称:Set Cardinality(集合基数) 获取一个集合里有多少个元素(统计集合大小) |
||
示例
127.0.0.1:6379> SADD myset a b c d # 添加4个元素
(integer) 4
127.0.0.1:6379> SCARD myset # 返回:4
(integer) 4
- 如果集合 不存在,返回 0
- 时间复杂度 O(1),超级快(Redis 内部直接存了长度)
业务场景
- 统计点赞数
- 统计在线人数
- 统计标签数量
- 统计好友数量
6.11 SISMEMBER 命令
|
命令 |
SISMEMBER key member |
时间复杂度 |
O(1) |
|
描述 |
全称:Set Is Member(是不是集合成员)判断一个元素 是否存在于集合中 |
||
127.0.0.1:6379> SADD myset a b c
(integer) 3
127.0.0.1:6379> SISMEMBER myset a
(integer) 1 # 1 表示存在
127.0.0.1:6379> SISMEMBER myset z
(integer) 0 # 0 表示不存在更多推荐




所有评论(0)