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)

描述

返回存储在 key 处的值的类型对应的字符串表示形式。可能返回的类型包括: stringlistsetzsethashstreamvectorset

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)

描述

将指定的字段值设置为存储在 key 中的哈希表中对应的数值

Redis 里面的 Hash 数据类型,可以理解成 Redis 里面的 JSON 数据结构。可以存储一个对象。案例如下:

HSET member:toast name zhangsan age 16 job Teacher

这样一个key 存储了一个对象,而不是三个key存储一个对象,这样节省存储空间

3.2 HGET 命令

命令

HGET key field

时间复杂度

O(1)

描述

返回存储在 key 中的哈希表中、与 field 相关联的值。

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)

描述

返回存储在 key 处的哈希表中的所有字段和值。在返回的值中,每个字段名后面都跟着该字段的值。

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)

描述

返回存储在 key 中的哈希表中、与指定键 fields 相关联的值。

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)

描述

返回存储在 key 中的哈希表中所有的字段名称。

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)

描述

返回存储在 key 处的哈希表中包含的字段数量。

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)

描述

从存储在 key 处的哈希表中删除指定的字段。如果该哈希表中不存在这些字段,则会忽略它们。

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)

描述

返回存储在 key 中的哈希表中的所有值。

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)

描述

如果 field 是存储在 key 中的哈希表中已存在的字段,则返回 1。否则为 0

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 处的数值自增加 1。如果该键不存在,则在执行该操作之前,会先将其设置为 0

对指定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 处的数值减 1。如果该键不存在,则在执行该操作之前,会先将其设置为 0

对指定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 处的数值自增指定步长(increment)。如果该键不存在,则在执行该操作之前,会先将其设置为 0

对指定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 处的数值自减指定步长(increment)。如果该键不存在,则在执行该操作之前,会先将其设置为 0

对指定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)

描述

将存储在 key 处的浮点数所对应的字符串值增加指定的 increment 数值。如果使用负数的 increment 值,那么存储在该键处的数值将会减少(这是加法的本质属性所决定的)。

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 ...]

描述

key 队列左边追加元素,从Redis2.4.0 开始可以支持追加多个元素,元素依次往左排序

命令

LPOP key [count]

描述

移除并返回存储在 key 中的列表中的第一个元素(左边的元素)

默认情况下,该命令会从列表的左边开头选取一个元素。如果提供了可选的 count 参数,那么回复将包含最多 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 ...]

描述

key 队列右边追加元素,从Redis2.4.0 开始可以支持追加多个元素,元素依次往右排序

命令

RPOP key [count]

描述

移除并返回存储在 key 中的列表中的最后一个元素(右边的元素)

默认情况下,该命令会从列表的(右边)最末尾的元素。如果提供了可选的 count 参数,那么回复将包含最多 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 的距离

描述

返回存储在 key 位置的列表中的指定元素。 startstop 是基于 0 的索引

LRANGERedis 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 表示元素个数

描述

将指定的成员添加到存储在 key 中的集合中。

如果这些成员已经属于该集合,那么它们将被忽略。

如果 key 不存在,那么在添加指定成员之前会先创建一个新的集合。

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 是需要移除的成员数量

描述

从存储在 key 中的集合中移除指定的成员。不属于该集合的成员将被忽略。如果 key 不存在,则将其视为空集合,此命令会返回 0 的值。

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 是需要弹出的成员数量

描述

默认情况下,该命令会从集合中取出一个成员。如果提供了可选的 count 参数,那么回复中将会包含最多 count 个成员,具体数量取决于集合的基数。

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 减去后面所有集合的差集,然后把结果存到 destination 这个新集合里。

差集规则:只保留【第一个集合有】、【其他所有集合都没有】的元素

结果 = 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"

业务场景

  1. 用户:待审核→已通过分组迁移;
  2. 商品:临时库存→正式上架分组挪动;
  3. 黑名单:临时拉黑→永久拉黑迁移。

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 表示不存在
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐