Redis 集合
提示
来自deepseek解释
原文链接:https://redis.io/docs/latest/develop/data-types/sets/
代码示例图例
以下代码示例展示了在不同编程语言和客户端库中执行相同操作的方式:
- Redis CLI:Redis 命令行界面
- C#(同步):StackExchange.Redis 同步客户端
- C#(异步):StackExchange.Redis 异步客户端
- Go:go-redis 客户端
- Java(同步 - Jedis):Jedis 同步客户端
- Java(异步 - Lettuce):Lettuce 异步客户端
- Java(响应式 - Lettuce):Lettuce 响应式/流式客户端
- JavaScript(Node.js):node-redis 客户端
- PHP:Predis 客户端
- Python:redis-py 客户端
- Rust(同步):redis-rs 同步客户端
- Rust(异步):redis-rs 异步客户端
每个代码示例都演示了不同语言中相同的基本操作。具体语法和模式因语言和客户端库而异,但底层的 Redis 命令和行为保持一致。
集合命令摘要
本组共 17 条命令:
| 命令 | 摘要 | 复杂度 | 起始版本 |
|---|---|---|---|
| SADD | 向集合添加一个或多个成员。如果键不存在则创建。 | 每个添加的元素为 O(1),因此添加 N 个元素为 O(N)... | 1.0.0 |
| SCARD | 返回集合中的成员数量。 | O(1) | 1.0.0 |
| SDIFF | 返回多个集合的差集。 | O(N),其中 N 是所有集合中的元素总数... | 1.0.0 |
| SDIFFSTORE | 将多个集合的差集存储到一个键中。 | O(N),其中 N 是所有集合中的元素总数... | 1.0.0 |
| SINTER | 返回多个集合的交集。 | 最坏情况 O(N*M),其中 N 是第一个集合的大小... | 1.0.0 |
| SINTERCARD | 返回多个集合交集的成员数量。 | 最坏情况 O(N*M)... | 7.0.0 |
| SINTERSTORE | 将多个集合的交集存储到一个键中。 | 最坏情况 O(N*M)... | 1.0.0 |
| SISMEMBER | 判断成员是否属于集合。 | O(1) | 1.0.0 |
| SMEMBERS | 返回集合的所有成员。 | O(N),其中 N 是集合的基数。 | 1.0.0 |
| SMISMEMBER | 判断多个成员是否属于集合。 | O(N),其中 N 是要检查的元素数量... | 6.2.0 |
| SMOVE | 将成员从一个集合移动到另一个集合。 | O(1) | 1.0.0 |
| SPOP | 移除并返回集合中的一个或多个随机成员。如果最后一个成员被弹出则删除集合。 | 不带 count 参数时为 O(1),否则 O(N)... | 1.0.0 |
| SRANDMEMBER | 从集合中获取一个或多个随机成员。 | 不带 count 参数时为 O(1),否则 O(N)... | 1.0.0 |
| SREM | 从集合中移除一个或多个成员。如果最后一个成员被移除则删除集合。 | O(N),其中 N 是要移除的成员数量... | 1.0.0 |
| SSCAN | 迭代集合中的成员。 | 每次调用 O(1)。完整迭代 O(N)... | 2.8.0 |
| SUNION | 返回多个集合的并集。 | O(N),其中 N 是所有集合中的元素总数... | 1.0.0 |
| SUNIONSTORE | 将多个集合的并集存储到一个键中。 | O(N),其中 N 是所有集合中的元素总数... | 1.0.0 |
Redis 集合是唯一字符串(成员)的无序集合。您可以使用 Redis 集合高效地:
- 跟踪唯一项目(例如,跟踪访问给定博客文章的所有唯一 IP 地址)。
- 表示关系(例如,具有给定角色的所有用户的集合)。
- 执行常见的集合操作,如交集、并集和差集。
示例
- 存储在法国和美国比赛的自行车集合。注意,如果添加已经存在的成员,它将被忽略。
基础:使用 SADD 向集合添加成员(重复项会被静默忽略)
难度: 初级
命令: SADD
复杂度: O(1)
可用客户端: Redis CLI, C#, Go, Java (异步 - Lettuce), Java (响应式 - Lettuce), Java (同步 - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (异步), Rust (同步)
Redis CLI
> SADD bikes:racing:france bike:1
(integer) 1
> SADD bikes:racing:france bike:1
(integer) 0
> SADD bikes:racing:france bike:2 bike:3
(integer) 2
> SADD bikes:racing:usa bike:1 bike:4
(integer) 2C#
long res1 = db.SetAdd("bikes:racing:france", ["bike:1"]);
Console.WriteLine(res1); // >>> 1
long res2 = db.SetAdd("bikes:racing:france", ["bike:1"]);
Console.WriteLine(res2); // >>> 0
long res3 = db.SetAdd("bikes:racing:france", ["bike:2", "bike:3"]);
Console.WriteLine(res3); // >>> 2
long res4 = db.SetAdd("bikes:racing:usa", ["bike:1", "bike:4"]);
Console.WriteLine(res4); // >>> 2Go
res1, err := rdb.SAdd(ctx, "bikes:racing:france", "bike:1").Result()
if err != nil {
panic(err)
}
fmt.Println(res1) // >>> 1
res2, err := rdb.SAdd(ctx, "bikes:racing:france", "bike:1").Result()
if err != nil {
panic(err)
}
fmt.Println(res2) // >>> 0
res3, err := rdb.SAdd(ctx, "bikes:racing:france", "bike:2", "bike:3").Result()
if err != nil {
panic(err)
}
fmt.Println(res3) // >>> 2
res4, err := rdb.SAdd(ctx, "bikes:racing:usa", "bike:1", "bike:4").Result()
if err != nil {
panic(err)
}
fmt.Println(res4) // >>> 2Java(异步 - Lettuce)
CompletableFuture<Void> sAdd = asyncCommands.sadd("bikes:racing:france", "bike:1").thenCompose(res1 -> {
System.out.println(res1); // >>> 1
return asyncCommands.sadd("bikes:racing:france", "bike:1");
}).thenCompose(res2 -> {
System.out.println(res2); // >>> 0
return asyncCommands.sadd("bikes:racing:france", "bike:2", "bike:3");
}).thenCompose(res3 -> {
System.out.println(res3); // >>> 2
return asyncCommands.sadd("bikes:racing:usa", "bike:1", "bike:4");
})
.thenAccept(System.out::println)
// >>> 2
.toCompletableFuture();Java(响应式 - Lettuce)
Mono<Void> sAdd = reactiveCommands.sadd("bikes:racing:france", "bike:1").doOnNext(result -> {
System.out.println(result); // >>> 1
}).flatMap(v -> reactiveCommands.sadd("bikes:racing:france", "bike:1")).doOnNext(result -> {
System.out.println(result); // >>> 0
}).flatMap(v -> reactiveCommands.sadd("bikes:racing:france", "bike:2", "bike:3")).doOnNext(result -> {
System.out.println(result); // >>> 2
}).flatMap(v -> reactiveCommands.sadd("bikes:racing:usa", "bike:1", "bike:4")).doOnNext(result -> {
System.out.println(result); // >>> 2
}).then();Java(同步 - Jedis)
long res1 = jedis.sadd("bikes:racing:france", "bike:1");
System.out.println(res1); // >>> 1
long res2 = jedis.sadd("bikes:racing:france", "bike:1");
System.out.println(res2); // >>> 0
long res3 = jedis.sadd("bikes:racing:france", "bike:2", "bike:3");
System.out.println(res3); // >>> 2
long res4 = jedis.sadd("bikes:racing:usa", "bike:1", "bike:4");
System.out.println(res4); // >>> 2JavaScript(Node.js)
const res1 = await client.sAdd('bikes:racing:france', 'bike:1')
console.log(res1) // >>> 1
const res2 = await client.sAdd('bikes:racing:france', 'bike:1')
console.log(res2) // >>> 0
const res3 = await client.sAdd('bikes:racing:france', ['bike:2', 'bike:3'])
console.log(res3) // >>> 2
const res4 = await client.sAdd('bikes:racing:usa', ['bike:1', 'bike:4'])
console.log(res4) // >>> 2PHP
$res1 = $r->sadd('bikes:racing:france', ['bike:1']);
echo $res1 . PHP_EOL;
// >>> 1
$res2 = $r->sadd('bikes:racing:france', ['bike:1']);
echo $res2 . PHP_EOL;
// >>> 0
$res3 = $r->sadd('bikes:racing:france', ['bike:2', 'bike:3']);
echo $res3 . PHP_EOL;
// >>> 2
$res4 = $r->sadd('bikes:racing:usa', ['bike:1', 'bike:4']);
echo $res4 . PHP_EOL;
// >>> 2Python
res1 = r.sadd("bikes:racing:france", "bike:1")
print(res1) # >>> 1
res2 = r.sadd("bikes:racing:france", "bike:1")
print(res2) # >>> 0
res3 = r.sadd("bikes:racing:france", "bike:2", "bike:3")
print(res3) # >>> 2
res4 = r.sadd("bikes:racing:usa", "bike:1", "bike:4")
print(res4) # >>> 2Ruby
res1 = r.sadd('bikes:racing:france', ['bike:1'])
puts res1 # 1
res2 = r.sadd('bikes:racing:france', ['bike:1'])
puts res2 # 0
res3 = r.sadd('bikes:racing:france', ['bike:2', 'bike:3'])
puts res3 # 2
res4 = r.sadd('bikes:racing:usa', ['bike:1', 'bike:4'])
puts res4 # 2Rust(异步)
if let Ok(res) = r.sadd("bikes:racing:france", "bike:1").await {
let res: usize = res;
println!("{res}"); // >>> 1
}
if let Ok(res) = r.sadd("bikes:racing:france", "bike:1").await {
let res: usize = res;
println!("{res}"); // >>> 0
}
if let Ok(res) = r.sadd("bikes:racing:france", &["bike:2", "bike:3"]).await {
let res: usize = res;
println!("{res}"); // >>> 2
}
if let Ok(res) = r.sadd("bikes:racing:usa", &["bike:1", "bike:4"]).await {
let res: usize = res;
println!("{res}"); // >>> 2
}Rust(同步)
if let Ok(res) = r.sadd("bikes:racing:france", "bike:1") {
let res: usize = res;
println!("{res}"); // >>> 1
}
if let Ok(res) = r.sadd("bikes:racing:france", "bike:1") {
let res: usize = res;
println!("{res}"); // >>> 0
}
if let Ok(res) = r.sadd("bikes:racing:france", &["bike:2", "bike:3"]) {
let res: usize = res;
println!("{res}"); // >>> 2
}
if let Ok(res) = r.sadd("bikes:racing:usa", &["bike:1", "bike:4"]) {
let res: usize = res;
println!("{res}"); // >>> 2
}- 检查 bike:1 或 bike:2 是否在美国比赛。
基础:使用 SISMEMBER 测试集合成员资格(O(1) 查找,比检查列表快得多)
难度: 初级
构建基础: sadd
命令: SISMEMBER
复杂度: O(1)
可用客户端: Redis CLI, C#, Go, Java (异步 - Lettuce), Java (响应式 - Lettuce), Java (同步 - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (异步), Rust (同步)
Redis CLI
> SISMEMBER bikes:racing:usa bike:1
(integer) 1
> SISMEMBER bikes:racing:usa bike:2
(integer) 0C#
bool res5 = db.SetContains("bikes:racing:france", "bike:1");
Console.WriteLine(res5); // >>> True
bool res6 = db.SetContains("bikes:racing:usa", "bike:2");
Console.WriteLine(res6); // >>> FalseGo
res5, err := rdb.SIsMember(ctx, "bikes:racing:usa", "bike:1").Result()
if err != nil {
panic(err)
}
fmt.Println(res5) // >>> true
res6, err := rdb.SIsMember(ctx, "bikes:racing:usa", "bike:2").Result()
if err != nil {
panic(err)
}
fmt.Println(res6) // >>> falseJava(异步 - Lettuce)
CompletableFuture<Void> sIsMember = sAdd.thenCompose(r -> {
return asyncCommands.sismember("bikes:racing:usa", "bike:1");
}).thenCompose(res4 -> {
System.out.println(res4); // >>> true
return asyncCommands.sismember("bikes:racing:usa", "bike:2");
})
.thenAccept(System.out::println) // >>> false
.toCompletableFuture();Java(响应式 - Lettuce)
Mono<Boolean> sIsMember1 = reactiveCommands.sismember("bikes:racing:usa", "bike:1").doOnNext(result -> {
System.out.println(result); // >>> true
});
Mono<Boolean> sIsMember2 = reactiveCommands.sismember("bikes:racing:usa", "bike:2").doOnNext(result -> {
System.out.println(result); // >>> false
});Java(同步 - Jedis)
jedis.sadd("bikes:racing:france", "bike:1", "bike:2", "bike:3");
jedis.sadd("bikes:racing:usa", "bike:1", "bike:4");
boolean res5 = jedis.sismember("bikes:racing:usa", "bike:1");
System.out.println(res5); // >>> true
boolean res6 = jedis.sismember("bikes:racing:usa", "bike:2");
System.out.println(res6); // >>> falseJavaScript(Node.js)
await client.del('bikes:racing:france')
await client.del('bikes:racing:usa')
await client.sAdd('bikes:racing:france', ['bike:1', 'bike:2', 'bike:3'])
await client.sAdd('bikes:racing:usa', ['bike:1', 'bike:4'])
const res5 = await client.sIsMember('bikes:racing:usa', 'bike:1')
console.log(res5) // >>> 1
const res6 = await client.sIsMember('bikes:racing:usa', 'bike:2')
console.log(res6) // >>> 0PHP
$res5 = $r->sismember('bikes:racing:usa', 'bike:1');
echo $res5 . PHP_EOL;
// >>> 1
$res6 = $r->sismember('bikes:racing:usa', 'bike:2');
echo $res6 . PHP_EOL;
// >>> 0Python
r.sadd("bikes:racing:france", "bike:1", "bike:2", "bike:3")
r.sadd("bikes:racing:usa", "bike:1", "bike:4")
res5 = r.sismember("bikes:racing:usa", "bike:1")
print(res5) # >>> 1
res6 = r.sismember("bikes:racing:usa", "bike:2")
print(res6) # >>> 0Ruby
r.del('bikes:racing:france', 'bikes:racing:usa')
r.sadd('bikes:racing:france', ['bike:1', 'bike:2', 'bike:3'])
r.sadd('bikes:racing:usa', ['bike:1', 'bike:4'])
res5 = r.sismember('bikes:racing:usa', 'bike:1')
puts res5 # true
res6 = r.sismember('bikes:racing:usa', 'bike:2')
puts res6 # falseRust(异步)
if let Ok(res) = r.sismember("bikes:racing:usa", "bike:1").await {
let res: bool = res;
println!("{}", i32::from(res)); // >>> 1
}
if let Ok(res) = r.sismember("bikes:racing:usa", "bike:2").await {
let res: bool = res;
println!("{}", i32::from(res)); // >>> 0
}Rust(同步)
if let Ok(res) = r.sismember("bikes:racing:usa", "bike:1") {
let res: bool = res;
println!("{}", i32::from(res)); // >>> 1
}
if let Ok(res) = r.sismember("bikes:racing:usa", "bike:2") {
let res: bool = res;
println!("{}", i32::from(res)); // >>> 0
}- 哪些自行车同时参加了两场比赛?
集合交集:使用 SINTER 查找多个集合的共同成员(例如,多个组中的用户)
难度: 中级
构建基础: sadd
命令: SINTER
复杂度: O(N*M)
可用客户端: Redis CLI, C#, Go, Java (异步 - Lettuce), Java (响应式 - Lettuce), Java (同步 - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (异步), Rust (同步)
Redis CLI
> SINTER bikes:racing:france bikes:racing:usa
1) "bike:1"C#
long res7 = db.SetAdd("{bikes:racing}:france", ["bike:1", "bike:2", "bike:3"]);
long res8 = db.SetAdd("{bikes:racing}:usa", ["bike:1", "bike:4"]);
RedisValue[] res9 = db.SetCombine(SetOperation.Intersect, ["{bikes:racing}:france", "{bikes:racing}:usa"]);
Console.WriteLine(string.Join(", ", res9)); // >>> bike:1Go
res7, err := rdb.SInter(ctx, "bikes:racing:france", "bikes:racing:usa").Result()
if err != nil {
panic(err)
}
fmt.Println(res7) // >>> [bike:1]Java(异步 - Lettuce)
CompletableFuture<Void> sInter = sIsMember.thenCompose(r -> {
return asyncCommands.sinter("bikes:racing:france", "bikes:racing:usa");
})
.thenAccept(System.out::println) // >>> ["bike:1"]
.toCompletableFuture();Java(响应式 - Lettuce)
Mono<List<String>> sInter = reactiveCommands.sinter("bikes:racing:france", "bikes:racing:usa").collectList()
.doOnNext(result -> {
System.out.println(result); // >>> [bike:1]
});Java(同步 - Jedis)
jedis.sadd("bikes:racing:france", "bike:1", "bike:2", "bike:3");
jedis.sadd("bikes:racing:usa", "bike:1", "bike:4");
Set<String> res7 = jedis.sinter("bikes:racing:france", "bikes:racing:usa");
System.out.println(res7); // >>> [bike:1]JavaScript(Node.js)
await client.del('bikes:racing:france')
await client.del('bikes:racing:usa')
await client.sAdd('bikes:racing:france', ['bike:1', 'bike:2', 'bike:3'])
await client.sAdd('bikes:racing:usa', ['bike:1', 'bike:4'])
const res7 = await client.sInter(['bikes:racing:france', 'bikes:racing:usa'])
console.log(res7) // >>> {'bike:1'}PHP
$res7 = $r->sinter(['bikes:racing:france', 'bikes:racing:usa']);
echo json_encode($res7) . PHP_EOL;
// >>> ["bike:1"]Python
r.sadd("bikes:racing:france", "bike:1", "bike:2", "bike:3")
r.sadd("bikes:racing:usa", "bike:1", "bike:4")
res7 = r.sinter("bikes:racing:france", "bikes:racing:usa")
print(res7) # >>> {'bike:1'}Ruby
r.del('bikes:racing:france', 'bikes:racing:usa')
r.sadd('bikes:racing:france', ['bike:1', 'bike:2', 'bike:3'])
r.sadd('bikes:racing:usa', ['bike:1', 'bike:4'])
res7 = r.sinter('bikes:racing:france', 'bikes:racing:usa')
puts res7.inspect # ["bike:1"]Rust(异步)
if let Ok(res) = r.sinter(&["bikes:racing:france", "bikes:racing:usa"]).await {
let res: HashSet<String> = res;
let res = sorted_set(res);
println!("{res:?}"); // >>> ["bike:1"]
}Rust(同步)
if let Ok(res) = r.sinter(&["bikes:racing:france", "bikes:racing:usa"]) {
let res: HashSet<String> = res;
let res = sorted_set(res);
println!("{res:?}"); // >>> ["bike:1"]
}- 法国有多少辆自行车在比赛?
基础:使用 SCARD 获取集合基数(高效计数唯一项)
难度: 初级
构建基础: sadd
命令: SCARD
复杂度: O(1)
可用客户端: Redis CLI, C#, Go, Java (异步 - Lettuce), Java (响应式 - Lettuce), Java (同步 - Jedis), JavaScript (Node.js), PHP, Python, Ruby, Rust (异步), Rust (同步)
Redis CLI
> SCARD bikes:racing:france
(integer) 3C#
long res10 = db.SetAdd("bikes:racing:france", ["bike:1", "bike:2", "bike:3"]);
long res11 = db.SetLength("bikes:racing:france");
Console.WriteLine(res11); // >>> 3Go
res8, err := rdb.SCard(ctx, "bikes:racing:france").Result()
if err != nil {
panic(err)
}
fmt.Println(res8) // >>> 3Java(异步 - Lettuce)
CompletableFuture<Void> sCard = sInter.thenCompose(r -> {
return asyncCommands.scard("bikes:racing:france");
})
.thenAccept(System.out::println) // >>> 3
.toCompletableFuture();Java(响应式 - Lettuce)
Mono<Long> sCard = reactiveCommands.scard("bikes:racing:france").doOnNext(result -> {
System.out.println(result); // >>> 3
});Java(同步 - Jedis)
jedis.sadd("bikes:racing:france", "bike:1", "bike:2", "bike:3");
long res8 = jedis.scard("bikes:racing:france");
System.out.println(res8); // >>> 3JavaScript(Node.js)
await client.del('bikes:racing:france')
await client.sAdd('bikes:racing:france', ['bike:1', 'bike:2', 'bike:3'])
const res8 = await client.sCard('bikes:racing:france')
console.log(res8) // >>> 3PHP
$res8 = $r->scard('bikes:racing:france');
echo $res8 . PHP_EOL;
// >>> 3Python
r.sadd("bikes:racing:france", "bike:1", "bike:2", "bike:3")
res8 = r.scard("bikes:racing:france")
print(res8) # >>> 3Ruby
r.del('bikes:racing:france')
r.sadd('bikes:racing:france', ['bike:1', 'bike:2', 'bike:3'])
res8 = r.scard('bikes:racing:france')
puts res8 # 3Rust(异步)
if let Ok(res) = r.scard("bikes:racing:france").await {
let res: usize = res;
println!("{res}"); // >>> 3
}Rust(同步)
if let Ok(res) = r.scard("bikes:racing:france") {
let res: usize = res;
println!("{res}"); // >>> 3
}