Redis 数组
提示
来自deepseek解释
原文链接:https://redis.io/docs/latest/develop/data-types/arrays/
代码示例图例
下面的代码示例展示了如何在不同编程语言和客户端库中执行相同的操作:
- 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 命令和行为保持一致。
数组命令摘要
本组共 18 条命令:
| 命令 | 摘要 | 复杂度 | 起始版本 |
|---|---|---|---|
| ARCOUNT | 返回数组中非空元素的数量。 | O(1) | 8.8.0 |
| ARDEL | 删除数组中指定索引处的元素。 | O(N),其中 N 是要删除的索引数量 | 8.8.0 |
| ARDELRANGE | 删除一个或多个范围内的元素。 | 与范围内现有元素数量成正比... | 8.8.0 |
| ARGET | 获取数组中索引处的值。 | O(1) | 8.8.0 |
| ARGETRANGE | 获取索引范围内的值。 | O(N),其中 N 是范围长度 | 8.8.0 |
| ARGREP | 使用文本谓词在范围内搜索数组元素。 | O(P * C),其中 P 是访问的位置数... | 8.8.0 |
| ARINFO | 返回数组的元数据。 | O(1),或使用 FULL 选项时为 O(N),其中 N 是切片数量 | 8.8.0 |
| ARINSERT | 在连续索引处插入一个或多个值。 | O(N),其中 N 是值的数量 | 8.8.0 |
| ARLASTITEMS | 返回最近插入的元素。 | O(N),其中 N 是数量 | 8.8.0 |
| ARLEN | 返回数组的长度(最大索引 + 1)。 | O(1) | 8.8.0 |
数组命令摘要(第 2 部分)
| 命令 | 摘要 | 复杂度 | 起始版本 |
|---|---|---|---|
| ARMGET | 获取数组中多个索引处的值。 | O(N),其中 N 是索引数量 | 8.8.0 |
| ARMSET | 在数组中设置多个索引-值对。 | O(N),其中 N 是对的数量 | 8.8.0 |
| ARNEXT | 返回 ARINSERT 将要使用的下一个索引。 | O(1) | 8.8.0 |
| AROP | 对范围内的数组元素执行聚合操作。 | O(P),其中 P 是访问的位置数... | 8.8.0 |
| ARRING | 将值插入指定大小的环形缓冲区,根据需要回绕和截断。 | 通常为 O(M),环形缓冲区调整大小时为 O(N+M)... | 8.8.0 |
| ARSCAN | 迭代范围内存在的元素,返回索引-值对。 | O(P),其中 P 是访问的位置数... | 8.8.0 |
| ARSEEK | 将 ARINSERT / ARRING 游标设置为特定索引。 | O(1) | 8.8.0 |
| ARSET | 在数组中从某个索引开始设置一个或多个连续值。 | O(N),其中 N 是值的数量 | 8.8.0 |
Redis 数组是稀疏的、可索引寻址的数据结构,将整数索引(范围为 0 到 2⁶⁴−1)映射到字符串值。与列表不同,元素直接通过索引访问,而不是通过序列中的位置,并且您可以设置任何索引而无需为已占用槽之间的间隙分配内存。这使得数组非常适合时间戳事件日志、流式测量的环形缓冲区、滑动窗口分析以及其他涉及稀疏或高索引访问模式的工作负载。
基本用法
使用 ARSET 从给定索引开始写入一个或多个连续值,使用 ARGET 读取索引处的值。访问未设置的索引返回 nil 回复。
使用 ARSET 写入连续值,使用 ARGET 读取单个索引;未设置的索引返回 nil
命令: ARSET, ARGET
复杂度:
- ARSET: O(N)
- ARGET: O(1)
可用客户端: Redis CLI, Go, Java (异步 - Lettuce), Java (响应式 - Lettuce), JavaScript (Node.js), PHP, Python
Redis CLI
> ARSET events:1 0 "login" "click" "purchase"
(integer) 3
> ARGET events:1 0
"login"
> ARGET events:1 999
(nil)Go
setRes, err := rdb.ARSet(ctx, "events:1", 0, "login", "click", "purchase").Result()
if err != nil {
panic(err)
}
fmt.Println(setRes) // >>> 3
getRes, err := rdb.ARGet(ctx, "events:1", 0).Result()
if err != nil {
panic(err)
}
fmt.Println(getRes) // >>> login
missing, err := rdb.ARGet(ctx, "events:1", 999).Result()
if err == redis.Nil {
fmt.Println("<nil>") // >>> <nil>
} else if err != nil {
panic(err)
} else {
fmt.Println(missing)
}Java(异步 - Lettuce)
CompletableFuture<Void> arsetArgetExample = asyncCommands
.arset("events:1", 0, "login", "click", "purchase")
.thenCompose(res1 -> {
System.out.println(res1);
// >>> 3
return asyncCommands.arget("events:1", 0);
})
.thenCompose(res2 -> {
System.out.println(res2);
// >>> login
return asyncCommands.arget("events:1", 999);
})
.thenAccept(res3 -> {
System.out.println(res3);
// >>> null
})
.toCompletableFuture();
arsetArgetExample.join();Java(响应式 - Lettuce)
Mono<Long> arsetArget1 = reactiveCommands.arset("events:1", 0, "login", "click", "purchase")
.doOnNext(result -> {
System.out.println(result); // >>> 3
});
arsetArget1.block();
Mono<String> arsetArget2 = reactiveCommands.arget("events:1", 0).doOnNext(result -> {
System.out.println(result); // >>> login
});
arsetArget2.block();
Mono<Optional<String>> arsetArget3 = reactiveCommands.arget("events:1", 999)
.map(Optional::of)
.defaultIfEmpty(Optional.empty())
.doOnNext(result -> {
System.out.println(result.orElse(null)); // >>> null
});
arsetArget3.block();JavaScript(Node.js)
const setResult = await client.arSet('events:1', 0, ['login', 'click', 'purchase']);
console.log(setResult); // >>> 3
const getResult = await client.arGet('events:1', 0);
console.log(getResult); // >>> login
const missingResult = await client.arGet('events:1', 999);
console.log(missingResult); // >>> nullPHP
$res1 = $redis->arset('events:1', 0, ['login', 'click', 'purchase']);
echo $res1 . PHP_EOL; // >>> 3
$res2 = $redis->arget('events:1', 0);
echo $res2 . PHP_EOL; // >>> login
$res3 = $redis->arget('events:1', 999);
echo var_export($res3, true) . PHP_EOL; // >>> NULLPython
res1 = r.arset("events:1", 0, "login", "click", "purchase")
print(res1)
# >>> 3
res2 = r.arget("events:1", 0)
print(res2)
# >>> login
res3 = r.arget("events:1", 999)
print(res3)
# >>> None要在任意非连续索引处写入值,请使用 ARMSET。要在一次往返中读取多个索引,请使用 ARMGET:
使用 ARMSET 写入任意非连续索引,使用 ARMGET 在一次往返中读取多个索引
命令: ARMSET, ARMGET
复杂度:
- ARMSET: O(N)
- ARMGET: O(N)
可用客户端: Redis CLI, Go, Java (异步 - Lettuce), Java (响应式 - Lettuce), JavaScript (Node.js), PHP, Python
Redis CLI
> ARMSET metrics 0 "10" 5 "20" 100 "30"
(integer) 3
> ARMGET metrics 0 5 100 999
1) "10"
2) "20"
3) "30"
4) (nil)Go
msetRes, err := rdb.ARMSet(ctx, "metrics",
redis.AREntry{Index: 0, Value: "10"},
redis.AREntry{Index: 5, Value: "20"},
redis.AREntry{Index: 100, Value: "30"},
).Result()
if err != nil {
panic(err)
}
fmt.Println(msetRes) // >>> 3
mgetRes, err := rdb.ARMGet(ctx, "metrics", 0, 5, 100, 999).Result()
if err != nil {
panic(err)
}
fmt.Println(mgetRes) // >>> [10 20 30 <nil>]Java(异步 - Lettuce)
Map<Long, String> metricsValues = new HashMap<>();
metricsValues.put(0L, "10");
metricsValues.put(5L, "20");
metricsValues.put(100L, "30");
CompletableFuture<Void> armsetArmgetExample = asyncCommands
.armset("metrics", metricsValues)
.thenCompose(res1 -> {
System.out.println(res1);
// >>> 3
return asyncCommands.armget("metrics", 0, 5, 100, 999);
})
.thenAccept(res2 -> {
System.out.println(res2);
// >>> [10, 20, 30, null]
})
.toCompletableFuture();
armsetArmgetExample.join();Java(响应式 - Lettuce)
Map<Long, String> armsetParams = new HashMap<>();
armsetParams.put(0L, "10");
armsetParams.put(5L, "20");
armsetParams.put(100L, "30");
Mono<Long> armsetArmget1 = reactiveCommands.armset("metrics", armsetParams).doOnNext(result -> {
System.out.println(result); // >>> 3
});
armsetArmget1.block();
Mono<List<String>> armsetArmget2 = reactiveCommands.armget("metrics", 0, 5, 100, 999)
.collectList()
.map(values -> values.stream()
.map(value -> value.getValueOrElse(null))
.collect(Collectors.toList()))
.doOnNext(result -> {
System.out.println(result); // >>> [10, 20, 30, null]
});
armsetArmget2.block();JavaScript(Node.js)
const mSetResult = await client.arMSet('metrics', { 0: '10', 5: '20', 100: '30' });
console.log(mSetResult); // >>> 3
const mGetResult = await client.arMGet('metrics', [0, 5, 100, 999]);
console.log(mGetResult); // >>> [ '10', '20', '30', null ]PHP
$res1 = $redis->armset('metrics', [0 => '10', 5 => '20', 100 => '30']);
echo $res1 . PHP_EOL; // >>> 3
$res2 = $redis->armget('metrics', [0, 5, 100, 999]);
echo json_encode($res2) . PHP_EOL; // >>> ["10","20","30",null]Python
res4 = r.armset("metrics", {0: "10", 5: "20", 100: "30"})
print(res4)
# >>> 3
res5 = r.armget("metrics", 0, 5, 100, 999)
print(res5)
# >>> ['10', '20', '30', None]数组长度 vs 元素数量
Redis 数组暴露两种不同的尺寸度量:
对于稀疏数组,这两个值可能差异很大:
比较稀疏数组的逻辑长度(ARLEN)与非空元素数量(ARCOUNT)
命令: ARSET, ARLEN, ARCOUNT
复杂度:
- ARSET: O(N)
- ARLEN: O(1)
- ARCOUNT: O(1)
可用客户端: Redis CLI, Go, Java (异步 - Lettuce), Java (响应式 - Lettuce), JavaScript (Node.js), PHP, Python
Redis CLI
> ARSET sparse 0 "a"
(integer) 1
> ARSET sparse 1000000 "b"
(integer) 1
> ARLEN sparse
(integer) 1000001
> ARCOUNT sparse
(integer) 2Go
set1, err := rdb.ARSet(ctx, "sparse", 0, "a").Result()
if err != nil {
panic(err)
}
fmt.Println(set1) // >>> 1
set2, err := rdb.ARSet(ctx, "sparse", 1000000, "b").Result()
if err != nil {
panic(err)
}
fmt.Println(set2) // >>> 1
lenRes, err := rdb.ARLen(ctx, "sparse").Result()
if err != nil {
panic(err)
}
fmt.Println(lenRes) // >>> 1000001
countRes, err := rdb.ARCount(ctx, "sparse").Result()
if err != nil {
panic(err)
}
fmt.Println(countRes) // >>> 2Java(异步 - Lettuce)
CompletableFuture<Void> lenCountExample = asyncCommands
.arset("sparse", 0, "a")
.thenCompose(res1 -> {
System.out.println(res1);
// >>> 1
return asyncCommands.arset("sparse", 1000000, "b");
})
.thenCompose(res2 -> {
System.out.println(res2);
// >>> 1
return asyncCommands.arlen("sparse");
})
.thenCompose(res3 -> {
System.out.println(res3);
// >>> 1000001
return asyncCommands.arcount("sparse");
})
.thenAccept(res4 -> {
System.out.println(res4);
// >>> 2
})
.toCompletableFuture();
lenCountExample.join();Java(响应式 - Lettuce)
Mono<Long> lenCount1 = reactiveCommands.arset("sparse", 0, "a").doOnNext(result -> {
System.out.println(result); // >>> 1
});
lenCount1.block();
Mono<Long> lenCount2 = reactiveCommands.arset("sparse", 1000000, "b").doOnNext(result -> {
System.out.println(result); // >>> 1
});
lenCount2.block();
Mono<Long> lenCount3 = reactiveCommands.arlen("sparse").doOnNext(result -> {
System.out.println(result); // >>> 1000001
});
lenCount3.block();
Mono<Long> lenCount4 = reactiveCommands.arcount("sparse").doOnNext(result -> {
System.out.println(result); // >>> 2
});
lenCount4.block();JavaScript(Node.js)
const setA = await client.arSet('sparse', 0, 'a');
console.log(setA); // >>> 1
const setB = await client.arSet('sparse', 1000000, 'b');
console.log(setB); // >>> 1
const lenResult = await client.arLen('sparse');
console.log(lenResult); // >>> 1000001
const countResult = await client.arCount('sparse');
console.log(countResult); // >>> 2PHP
$res1 = $redis->arset('sparse', 0, 'a');
echo $res1 . PHP_EOL; // >>> 1
$res2 = $redis->arset('sparse', 1000000, 'b');
echo $res2 . PHP_EOL; // >>> 1
$res3 = $redis->arlen('sparse');
echo $res3 . PHP_EOL; // >>> 1000001
$res4 = $redis->arcount('sparse');
echo $res4 . PHP_EOL; // >>> 2Python
res6 = r.arset("sparse", 0, "a")
print(res6)
# >>> 1
res7 = r.arset("sparse", 1000000, "b")
print(res7)
# >>> 1
res8 = r.arlen("sparse")
print(res8)
# >>> 1000001
res9 = r.arcount("sparse")
print(res9)
# >>> 2读取范围
ARGETRANGE 按索引顺序返回范围内的每个位置——包括空槽(返回 nil)。反转 start 和 end 会反转方向:
使用 ARGETRANGE 读取范围内每个位置,空槽返回 nil
命令: ARMSET, ARGETRANGE
复杂度:
- ARMSET: O(N)
- ARGETRANGE: O(N)
可用客户端: Redis CLI, Go, Java (异步 - Lettuce), Java (响应式 - Lettuce), JavaScript (Node.js), PHP, Python
Redis CLI
> ARMSET seq 0 "a" 1 "b" 3 "d"
(integer) 3
> ARGETRANGE seq 0 3
1) "a"
2) "b"
3) (nil)
4) "d"Go
msetRes, err := rdb.ARMSet(ctx, "seq",
redis.AREntry{Index: 0, Value: "a"},
redis.AREntry{Index: 1, Value: "b"},
redis.AREntry{Index: 3, Value: "d"},
).Result()
if err != nil {
panic(err)
}
fmt.Println(msetRes) // >>> 3
rangeRes, err := rdb.ARGetRange(ctx, "seq", 0, 3).Result()
if err != nil {
panic(err)
}
fmt.Println(rangeRes) // >>> [a b <nil> d]Java(异步 - Lettuce)
Map<Long, String> seqRangeValues = new HashMap<>();
seqRangeValues.put(0L, "a");
seqRangeValues.put(1L, "b");
seqRangeValues.put(3L, "d");
CompletableFuture<Void> argetrangeExample = asyncCommands
.armset("seq", seqRangeValues)
.thenCompose(res1 -> {
System.out.println(res1);
// >>> 3
return asyncCommands.argetrange("seq", 0, 3);
})
.thenAccept(res2 -> {
System.out.println(res2);
// >>> [a, b, null, d]
})
.toCompletableFuture();
argetrangeExample.join();Java(响应式 - Lettuce)
Map<Long, String> argetrangeParams = new HashMap<>();
argetrangeParams.put(0L, "a");
argetrangeParams.put(1L, "b");
argetrangeParams.put(3L, "d");
Mono<Long> argetrange1 = reactiveCommands.armset("seq", argetrangeParams).doOnNext(result -> {
System.out.println(result); // >>> 3
});
argetrange1.block();
Mono<List<String>> argetrange2 = reactiveCommands.argetrange("seq", 0, 3)
.collectList()
.map(values -> values.stream()
.map(value -> value.getValueOrElse(null))
.collect(Collectors.toList()))
.doOnNext(result -> {
System.out.println(result); // >>> [a, b, null, d]
});
argetrange2.block();JavaScript(Node.js)
const rangeSetResult = await client.arMSet('seq', { 0: 'a', 1: 'b', 3: 'd' });
console.log(rangeSetResult); // >>> 3
const rangeResult = await client.arGetRange('seq', 0, 3);
console.log(rangeResult); // >>> [ 'a', 'b', null, 'd' ]PHP
$res1 = $redis->armset('seq', [0 => 'a', 1 => 'b', 3 => 'd']);
echo $res1 . PHP_EOL; // >>> 3
$res2 = $redis->argetrange('seq', 0, 3);
echo json_encode($res2) . PHP_EOL; // >>> ["a","b",null,"d"]Python
res10 = r.armset("seq", {0: "a", 1: "b", 3: "d"})
print(res10)
# >>> 3
res11 = r.argetrange("seq", 0, 3)
print(res11)
# >>> ['a', 'b', None, 'd']要仅迭代存在的元素并同时获取其索引和值,请使用 ARSCAN。它跳过空槽,返回交替的索引-值对的扁平列表,并可选择使用 LIMIT 来限制结果大小:
使用 ARSCAN 仅迭代存在的元素,同时返回每个索引及其值
构建基础: argetrange
命令: ARSCAN
复杂度:
- ARSCAN: O(P)
可用客户端: Redis CLI, Go, Java (异步 - Lettuce), Java (响应式 - Lettuce), JavaScript (Node.js), PHP, Python
Redis CLI
> ARSCAN seq 0 3
1) (integer) 0
2) "a"
3) (integer) 1
4) "b"
5) (integer) 3
6) "d"Go
msetRes, err := rdb.ARMSet(ctx, "seq",
redis.AREntry{Index: 0, Value: "a"},
redis.AREntry{Index: 1, Value: "b"},
redis.AREntry{Index: 3, Value: "d"},
).Result()
if err != nil {
panic(err)
}
fmt.Println(msetRes) // >>> 3
scanRes, err := rdb.ARScan(ctx, "seq", 0, 3, nil).Result()
if err != nil {
panic(err)
}
for _, entry := range scanRes {
fmt.Printf("%d -> %s\n", entry.Index, entry.Value)
}
// >>> 0 -> a
// >>> 1 -> b
// >>> 3 -> dJava(异步 - Lettuce)
Map<Long, String> seqScanValues = new HashMap<>();
seqScanValues.put(0L, "a");
seqScanValues.put(1L, "b");
seqScanValues.put(3L, "d");
CompletableFuture<Void> arscanExample = asyncCommands
.armset("seq", seqScanValues)
.thenCompose(res1 -> {
System.out.println(res1);
// >>> 3
return asyncCommands.arscan("seq", 0, 3);
})
.thenAccept(res2 -> {
for (IndexedValue<String> pair : res2) {
System.out.println(pair.getIndex() + " -> " + pair.getValue());
}
// >>> 0 -> a
// >>> 1 -> b
// >>> 3 -> d
})
.toCompletableFuture();
arscanExample.join();Java(响应式 - Lettuce)
Map<Long, String> arscanParams = new HashMap<>();
arscanParams.put(0L, "a");
arscanParams.put(1L, "b");
arscanParams.put(3L, "d");
Mono<Long> arscan1 = reactiveCommands.armset("seq", arscanParams).doOnNext(result -> {
System.out.println(result); // >>> 3
});
arscan1.block();
Mono<List<IndexedValue<String>>> arscan2 = reactiveCommands.arscan("seq", 0, 3)
.doOnNext(entry -> {
System.out.println(entry.getIndex() + " -> " + entry.getValue());
// >>> 0 -> a
// >>> 1 -> b
// >>> 3 -> d
})
.collectList()
;
arscan2.block();JavaScript(Node.js)
const scanSetResult = await client.arMSet('seq', { 0: 'a', 1: 'b', 3: 'd' });
console.log(scanSetResult); // >>> 3
const scanResult = await client.arScan('seq', 0, 3);
for (const { index, value } of scanResult) {
console.log(`${index} -> ${value}`);
}
// >>> 0 -> a
// >>> 1 -> b
// >>> 3 -> dPHP
$res1 = $redis->armset('seq', [0 => 'a', 1 => 'b', 3 => 'd']);
echo $res1 . PHP_EOL; // >>> 3
$res2 = $redis->arscan('seq', 0, 3);
foreach ($res2 as $pair) {
echo $pair[0] . ' -> ' . $pair[1] . PHP_EOL;
}
// >>> 0 -> a
// >>> 1 -> b
// >>> 3 -> dPython
res12 = r.armset("seq", {0: "a", 1: "b", 3: "d"})
print(res12)
# >>> 3
res13 = r.arscan("seq", 0, 3)
for index, value in res13:
print(f"{index} -> {value}")
# >>> 0 -> a
# >>> 1 -> b
# >>> 3 -> d顺序插入
ARINSERT 使用一个内部游标追加值,该游标在每次调用后自动前进。使用 ARNEXT 检查下一个插入的位置,使用 ARSEEK 重新定位游标:
使用自动前进游标的 ARINSERT 追加值,用 ARNEXT 检查游标,用 ARSEEK 重新定位
命令: ARINSERT, ARNEXT, ARSEEK
复杂度:
- ARINSERT: O(N)
- ARNEXT: O(1)
- ARSEEK: O(1)
可用客户端: Redis CLI, Go, Java (异步 - Lettuce), Java (响应式 - Lettuce), JavaScript (Node.js), PHP, Python
Redis CLI
> ARINSERT log "event1"
(integer) 0
> ARINSERT log "event2"
(integer) 1
> ARNEXT log
(integer) 2
> ARSEEK log 10
(integer) 1
> ARINSERT log "event3"
(integer) 10Go
ins1, err := rdb.ARInsert(ctx, "log", "event1").Result()
if err != nil {
panic(err)
}
fmt.Println(ins1) // >>> 0
ins2, err := rdb.ARInsert(ctx, "log", "event2").Result()
if err != nil {
panic(err)
}
fmt.Println(ins2) // >>> 1
nextRes, err := rdb.ARNext(ctx, "log").Result()
if err != nil {
panic(err)
}
fmt.Println(nextRes) // >>> 2
seekRes, err := rdb.ARSeek(ctx, "log", 10).Result()
if err != nil {
panic(err)
}
fmt.Println(seekRes) // >>> 1
ins3, err := rdb.ARInsert(ctx, "log", "event3").Result()
if err != nil {
panic(err)
}
fmt.Println(ins3) // >>> 10Java(异步 - Lettuce)
CompletableFuture<Void> arinsertExample = asyncCommands
.arinsert("log", "event1")
.thenCompose(res1 -> {
System.out.println(res1);
// >>> 0
return asyncCommands.arinsert("log", "event2");
})
.thenCompose(res2 -> {
System.out.println(res2);
// >>> 1
return asyncCommands.arnext("log");
})
.thenCompose(res3 -> {
System.out.println(res3);
// >>> 2
return asyncCommands.arseek("log", 10);
})
.thenCompose(res4 -> {
System.out.println(res4);
// >>> 1
return asyncCommands.arinsert("log", "event3");
})
.thenAccept(res5 -> {
System.out.println(res5);
// >>> 10
})
.toCompletableFuture();
arinsertExample.join();Java(响应式 - Lettuce)
Mono<Long> arinsert1 = reactiveCommands.arinsert("log", "event1").doOnNext(result -> {
System.out.println(result); // >>> 0
});
arinsert1.block();
Mono<Long> arinsert2 = reactiveCommands.arinsert("log", "event2").doOnNext(result -> {
System.out.println(result); // >>> 1
});
arinsert2.block();
Mono<Long> arinsert3 = reactiveCommands.arnext("log").doOnNext(result -> {
System.out.println(result); // >>> 2
});
arinsert3.block();
Mono<Long> arinsert4 = reactiveCommands.arseek("log", 10).doOnNext(result -> {
System.out.println(result); // >>> 1
});
arinsert4.block();
Mono<Long> arinsert5 = reactiveCommands.arinsert("log", "event3").doOnNext(result -> {
System.out.println(result); // >>> 10
});
arinsert5.block();JavaScript(Node.js)
const insert1 = await client.arInsert('log', 'event1');
console.log(insert1); // >>> 0
const insert2 = await client.arInsert('log', 'event2');
console.log(insert2); // >>> 1
const nextResult = await client.arNext('log');
console.log(nextResult); // >>> 2
const seekResult = await client.arSeek('log', 10);
console.log(seekResult); // >>> 1
const insert3 = await client.arInsert('log', 'event3');
console.log(insert3); // >>> 10PHP
$res1 = $redis->arinsert('log', 'event1');
echo $res1 . PHP_EOL; // >>> 0
$res2 = $redis->arinsert('log', 'event2');
echo $res2 . PHP_EOL; // >>> 1
$res3 = $redis->arnext('log');
echo $res3 . PHP_EOL; // >>> 2
$res4 = $redis->arseek('log', 10);
echo $res4 . PHP_EOL; // >>> 1
$res5 = $redis->arinsert('log', 'event3');
echo $res5 . PHP_EOL; // >>> 10Python
res14 = r.arinsert("log", "event1")
print(res14)
# >>> 0
res15 = r.arinsert("log", "event2")
print(res15)
# >>> 1
res16 = r.arnext("log")
print(res16)
# >>> 2
res17 = r.arseek("log", 10)
print(res17)
# >>> 1
res18 = r.arinsert("log", "event3")
print(res18)
# >>> 10环形缓冲区模式
ARRING 将数组转换为固定大小的循环缓冲区。每次调用在 insert_idx % size 处插入一个值,一旦窗口填满,回绕到索引 0 并覆盖最旧的条目:
使用 ARRING 维护固定大小的循环缓冲区,满时回绕并覆盖最旧条目
命令: ARRING, ARGET
复杂度:
- ARRING: O(M)
- ARGET: O(1)
可用客户端: Redis CLI, Go, Java (异步 - Lettuce), Java (响应式 - Lettuce), JavaScript (Node.js), PHP, Python
Redis CLI
> ARRING readings 3 "v0"
(integer) 0
> ARRING readings 3 "v1"
(integer) 1
> ARRING readings 3 "v2"
(integer) 2
> ARRING readings 3 "v3"
(integer) 0
> ARGET readings 0
"v3"Go
ring0, err := rdb.ARRing(ctx, "readings", 3, "v0").Result()
if err != nil {
panic(err)
}
fmt.Println(ring0) // >>> 0
ring1, err := rdb.ARRing(ctx, "readings", 3, "v1").Result()
if err != nil {
panic(err)
}
fmt.Println(ring1) // >>> 1
ring2, err := rdb.ARRing(ctx, "readings", 3, "v2").Result()
if err != nil {
panic(err)
}
fmt.Println(ring2) // >>> 2
ring3, err := rdb.ARRing(ctx, "readings", 3, "v3").Result()
if err != nil {
panic(err)
}
fmt.Println(ring3) // >>> 0
getRes, err := rdb.ARGet(ctx, "readings", 0).Result()
if err != nil {
panic(err)
}
fmt.Println(getRes) // >>> v3Java(异步 - Lettuce)
CompletableFuture<Void> arringExample = asyncCommands
.arring("readings", 3, "v0")
.thenCompose(res1 -> {
System.out.println(res1);
// >>> 0
return asyncCommands.arring("readings", 3, "v1");
})
.thenCompose(res2 -> {
System.out.println(res2);
// >>> 1
return asyncCommands.arring("readings", 3, "v2");
})
.thenCompose(res3 -> {
System.out.println(res3);
// >>> 2
return asyncCommands.arring("readings", 3, "v3");
})
.thenCompose(res4 -> {
System.out.println(res4);
// >>> 0
return asyncCommands.arget("readings", 0);
})
.thenAccept(res5 -> {
System.out.println(res5);
// >>> v3
})
.toCompletableFuture();
arringExample.join();Java(响应式 - Lettuce)
Mono<Long> arring1 = reactiveCommands.arring("readings", 3, "v0").doOnNext(result -> {
System.out.println(result); // >>> 0
});
arring1.block();
Mono<Long> arring2 = reactiveCommands.arring("readings", 3, "v1").doOnNext(result -> {
System.out.println(result); // >>> 1
});
arring2.block();
Mono<Long> arring3 = reactiveCommands.arring("readings", 3, "v2").doOnNext(result -> {
System.out.println(result); // >>> 2
});
arring3.block();
Mono<Long> arring4 = reactiveCommands.arring("readings", 3, "v3").doOnNext(result -> {
System.out.println(result); // >>> 0
});
arring4.block();
Mono<String> arring5 = reactiveCommands.arget("readings", 0).doOnNext(result -> {
System.out.println(result); // >>> v3
});
arring5.block();JavaScript(Node.js)
const ring0 = await client.arRing('readings', 3, 'v0');
console.log(ring0); // >>> 0
const ring1 = await client.arRing('readings', 3, 'v1');
console.log(ring1); // >>> 1
const ring2 = await client.arRing('readings', 3, 'v2');
console.log(ring2); // >>> 2
const ring3 = await client.arRing('readings', 3, 'v3');
console.log(ring3); // >>> 0
const ringGet = await client.arGet('readings', 0);
console.log(ringGet); // >>> v3PHP
$res1 = $redis->arring('readings', 3, 'v0');
echo $res1 . PHP_EOL; // >>> 0
$res2 = $redis->arring('readings', 3, 'v1');
echo $res2 . PHP_EOL; // >>> 1
$res3 = $redis->arring('readings', 3, 'v2');
echo $res3 . PHP_EOL; // >>> 2
$res4 = $redis->arring('readings', 3, 'v3');
echo $res4 . PHP_EOL; // >>> 0
$res5 = $redis->arget('readings', 0);
echo $res5 . PHP_EOL; // >>> v3Python
res19 = r.arring("readings", 3, "v0")
print(res19)
# >>> 0
res20 = r.arring("readings", 3, "v1")
print(res20)
# >>> 1
res21 = r.arring("readings", 3, "v2")
print(res21)
# >>> 2
res22 = r.arring("readings", 3, "v3")
print(res22)
# >>> 0
res23 = r.arget("readings", 0)
print(res23)
# >>> v3ARLASTITEMS 按时间顺序检索最近插入的 N 个元素。传递 REV 标志可反转顺序:
使用 ARLASTITEMS 检索最近插入的 N 个元素,可使用 REV 反转顺序
构建基础: arring
命令: ARLASTITEMS
复杂度:
- ARLASTITEMS: O(N)
可用客户端: Redis CLI, Go, Java (异步 - Lettuce), Java (响应式 - Lettuce), JavaScript (Node.js), PHP, Python
Redis CLI
> ARLASTITEMS readings 3
1) "v1"
2) "v2"
3) "v3"
> ARLASTITEMS readings 3 REV
1) "v3"
2) "v2"
3) "v1"Go
// 设置环形缓冲区:向大小为 3 的环形缓冲区插入 v0, v1, v2, v3。
for _, v := range []string{"v0", "v1", "v2", "v3"} {
if err := rdb.ARRing(ctx, "readings", 3, v).Err(); err != nil {
panic(err)
}
}
lastRes, err := rdb.ARLastItems(ctx, "readings", 3, false).Result()
if err != nil {
panic(err)
}
fmt.Println(lastRes) // >>> [v1 v2 v3]
lastRevRes, err := rdb.ARLastItems(ctx, "readings", 3, true).Result()
if err != nil {
panic(err)
}
fmt.Println(lastRevRes) // >>> [v3 v2 v1]Java(异步 - Lettuce)
CompletableFuture<Void> arlastitemsExample = asyncCommands
.arring("readings", 3, "v0")
.thenCompose(res1 -> asyncCommands.arring("readings", 3, "v1"))
.thenCompose(res2 -> asyncCommands.arring("readings", 3, "v2"))
.thenCompose(res3 -> asyncCommands.arring("readings", 3, "v3"))
.thenCompose(res4 -> asyncCommands.arlastitems("readings", 3))
.thenCompose(res5 -> {
System.out.println(res5);
// >>> [v1, v2, v3]
return asyncCommands.arlastitems("readings", 3, true);
})
.thenAccept(res6 -> {
System.out.println(res6);
// >>> [v3, v2, v1]
})
.toCompletableFuture();
arlastitemsExample.join();Java(响应式 - Lettuce)
// 设置环形缓冲区:向大小为 3 的环形缓冲区插入 v0, v1, v2, v3。
reactiveCommands.arring("readings", 3, "v0").block();
reactiveCommands.arring("readings", 3, "v1").block();
reactiveCommands.arring("readings", 3, "v2").block();
reactiveCommands.arring("readings", 3, "v3").block();
Mono<List<String>> arlastitems1 = reactiveCommands.arlastitems("readings", 3).collectList()
.doOnNext(result -> {
System.out.println(result); // >>> [v1, v2, v3]
});
arlastitems1.block();
Mono<List<String>> arlastitems2 = reactiveCommands.arlastitems("readings", 3, true).collectList()
.doOnNext(result -> {
System.out.println(result); // >>> [v3, v2, v1]
});
arlastitems2.block();JavaScript(Node.js)
await client.arRing('readings', 3, 'v0');
await client.arRing('readings', 3, 'v1');
await client.arRing('readings', 3, 'v2');
await client.arRing('readings', 3, 'v3');
const lastItems = await client.arLastItems('readings', 3);
console.log(lastItems); // >>> [ 'v1', 'v2', 'v3' ]
const lastItemsRev = await client.arLastItems('readings', 3, { REV: true });
console.log(lastItemsRev); // >>> [ 'v3', 'v2', 'v1' ]PHP
$redis->arring('readings', 3, 'v0');
$redis->arring('readings', 3, 'v1');
$redis->arring('readings', 3, 'v2');
$redis->arring('readings', 3, 'v3');
$res1 = $redis->arlastitems('readings', 3);
echo json_encode($res1) . PHP_EOL; // >>> ["v1","v2","v3"]
$res2 = $redis->arlastitems('readings', 3, true);
echo json_encode($res2) . PHP_EOL; // >>> ["v3","v2","v1"]Python
r.arring("readings", 3, "v0")
r.arring("readings", 3, "v1")
r.arring("readings", 3, "v2")
r.arring("readings", 3, "v3")
res24 = r.arlastitems("readings", 3)
print(res24)
# >>> ['v1', 'v2', 'v3']
res25 = r.arlastitems("readings", 3, rev=True)
print(res25)
# >>> ['v3', 'v2', 'v1']聚合操作
AROP 在连续的索引范围内执行单次聚合:
| 操作 | 描述 |
|---|---|
SUM | 数值求和 |
MIN | 最小数值 |
MAX | 最大数值 |
AND / OR / XOR | 对整数值进行按位操作 |
MATCH value | 等于 value 的元素计数 |
USED | 范围内非空元素的数量 |
在连续范围上使用 AROP 执行单次聚合,如 SUM、MAX 或 MATCH 计数
命令: ARMSET, AROP
复杂度:
- ARMSET: O(N)
- AROP: O(P)
可用客户端: Redis CLI, Go, Java (异步 - Lettuce), Java (响应式 - Lettuce), JavaScript (Node.js), PHP, Python
Redis CLI
> ARMSET scores 0 "10" 1 "20" 2 "30"
(integer) 3
> AROP scores 0 2 SUM
"60"
> AROP scores 0 2 MAX
"30"
> AROP scores 0 2 MATCH "10"
(integer) 1Go
msetRes, err := rdb.ARMSet(ctx, "scores",
redis.AREntry{Index: 0, Value: "10"},
redis.AREntry{Index: 1, Value: "20"},
redis.AREntry{Index: 2, Value: "30"},
).Result()
if err != nil {
panic(err)
}
fmt.Println(msetRes) // >>> 3
sumRes, err := rdb.AROpSum(ctx, "scores", 0, 2).Result()
if err != nil {
panic(err)
}
fmt.Println(sumRes) // >>> 60
maxRes, err := rdb.AROpMax(ctx, "scores", 0, 2).Result()
if err != nil {
panic(err)
}
fmt.Println(maxRes) // >>> 30
matchRes, err := rdb.AROpMatch(ctx, "scores", 0, 2, "10").Result()
if err != nil {
panic(err)
}
fmt.Println(matchRes) // >>> 1Java(异步 - Lettuce)
Map<Long, String> aropScores = new HashMap<>();
aropScores.put(0L, "10");
aropScores.put(1L, "20");
aropScores.put(2L, "30");
CompletableFuture<Void> aropExample = asyncCommands
.armset("scores", aropScores)
.thenCompose(res1 -> {
System.out.println(res1);
// >>> 3
return asyncCommands.aropAggregate("scores", 0, 2, ArAggregateType.SUM);
})
.thenCompose(res2 -> {
System.out.println(res2);
// >>> 60
return asyncCommands.aropAggregate("scores", 0, 2, ArAggregateType.MAX);
})
.thenCompose(res3 -> {
System.out.println(res3);
// >>> 30
return asyncCommands.aropCount("scores", 0, 2, "10");
})
.thenAccept(res4 -> {
System.out.println(res4);
// >>> 1
})
.toCompletableFuture();
aropExample.join();Java(响应式 - Lettuce)
Map<Long, String> aropParams = new HashMap<>();
aropParams.put(0L, "10");
aropParams.put(1L, "20");
aropParams.put(2L, "30");
Mono<Long> arop1 = reactiveCommands.armset("scores", aropParams).doOnNext(result -> {
System.out.println(result); // >>> 3
});
arop1.block();
Mono<String> arop2 = reactiveCommands.aropAggregate("scores", 0, 2, ArAggregateType.SUM)
.doOnNext(result -> {
System.out.println(result); // >>> 60
});
arop2.block();
Mono<String> arop3 = reactiveCommands.aropAggregate("scores", 0, 2, ArAggregateType.MAX)
.doOnNext(result -> {
System.out.println(result); // >>> 30
});
arop3.block();
Mono<Long> arop4 = reactiveCommands.aropCount("scores", 0, 2, "10").doOnNext(result -> {
System.out.println(result); // >>> 1
});
arop4.block();JavaScript(Node.js)
const opSetResult = await client.arMSet('scores', { 0: '10', 1: '20', 2: '30' });
console.log(opSetResult); // >>> 3
const sumResult = await client.arOp('scores', 0, 2, 'SUM');
console.log(sumResult); // >>> 60
const maxResult = await client.arOp('scores', 0, 2, 'MAX');
console.log(maxResult); // >>> 30
const matchResult = await client.arOp('scores', 0, 2, 'MATCH', '10');
console.log(matchResult); // >>> 1PHP
$res1 = $redis->armset('scores', [0 => '10', 1 => '20', 2 => '30']);
echo $res1 . PHP_EOL; // >>> 3
$res2 = $redis->arop('scores', 0, 2, 'SUM');
echo $res2 . PHP_EOL; // >>> 60
$res3 = $redis->arop('scores', 0, 2, 'MAX');
echo $res3 . PHP_EOL; // >>> 30
$res4 = $redis->arop('scores', 0, 2, 'MATCH', '10');
echo $res4 . PHP_EOL; // >>> 1Python
res26 = r.armset("scores", {0: "10", 1: "20", 2: "30"})
print(res26)
# >>> 3
res27 = r.arop("scores", 0, 2, ArrayAggregateOperations.SUM)
print(res27)
# >>> 60
res28 = r.arop("scores", 0, 2, ArrayAggregateOperations.MAX)
print(res28)
# >>> 30
res29 = r.arop("scores", 0, 2, ArrayAggregateOperations.MATCH, value="10")
print(res29)
# >>> 1