性能
提示
来自deepseek解释
原文链接:https://redis.io/docs/latest/develop/data-types/vector-sets/performance/
代码示例说明
以下代码示例展示了如何使用不同的编程语言和客户端库执行相同的操作。为简洁起见,本译文仅保留 Java 相关的示例(同步 Jedis、异步 Lettuce、响应式 Lettuce)。
每个代码示例均以不同语言展示相同的基本操作。具体语法和模式会因语言和客户端库而异,但底层的 Redis 命令和行为保持一致。
查询性能
使用 VSIM 的向量相似性查询默认是多线程的。Redis 最多使用 32 个线程来并行处理这些查询。
VSIM的性能随可用 CPU 核心数近乎线性扩展。- 对于包含 300 维向量、使用 int8 量化的 300 万条目集合,预计每秒可执行约 50,000 次相似性查询。
- 性能在很大程度上取决于
EF参数:- 较高的
EF可提高召回率,但会降低搜索速度。 - 较低的
EF返回更快的结果,但精度会降低。
- 较高的
插入性能
使用 VADD 命令插入向量在计算上比查询更昂贵:
- 插入默认是单线程的。
- 使用
CAS选项可将候选图搜索卸载到后台线程。 - 在单个节点上,每秒预计可执行数千次插入。
量化效果
量化对速度和内存都有很大影响:
Q8(默认):比FP32小 4 倍,召回率高,速度快BIN(二进制):比FP32小 32 倍,召回率较低,搜索最快NOQUANT(FP32):全精度,性能较慢,内存使用最高
选择最适合您在精度和效率之间取舍的量化模式。 下面的示例展示了不同模式对简单向量的影响。 注意,即使在 NOQUANT 模式下,由于浮点数舍入,值也会略有变化。
难度: 高级
命令: VADD, VEMB
可用客户端: Redis CLI, C#, Go, Java(异步 - Lettuce), Java(响应式 - Lettuce), Java(同步 - Jedis), JavaScript(Node.js), PHP, Python, Rust(异步), Rust(同步)
Java(同步 - Jedis)
java
boolean res31 = jedis.vadd("quantSetQ8", new float[] { 1.262185f, 1.958231f }, "quantElement",
new VAddParams().q8());
System.out.println(res31); // >>> true
List<Double> res32 = jedis.vemb("quantSetQ8", "quantElement");
System.out.println("Q8: " + res32);
// >>> Q8: [~1.264, ~1.958]
boolean res33 = jedis.vadd("quantSetNoQ", new float[] { 1.262185f, 1.958231f },
"quantElement", new VAddParams().noQuant());
System.out.println(res33); // >>> true
List<Double> res34 = jedis.vemb("quantSetNoQ", "quantElement");
System.out.println("NOQUANT: " + res34);
// >>> NOQUANT: [~1.262185, ~1.958231]
boolean res35 = jedis.vadd("quantSetBin", new float[] { 1.262185f, 1.958231f },
"quantElement", new VAddParams().bin());
System.out.println(res35); // >>> true
List<Double> res36 = jedis.vemb("quantSetBin", "quantElement");
System.out.println("BIN: " + res36);
// >>> BIN: [1, 1]Java(异步 - Lettuce)
java
VAddArgs q8Args = VAddArgs.Builder.quantizationType(QuantizationType.Q8);
CompletableFuture<Void> quantizationOperations = asyncCommands
.vadd("quantSetQ8", "quantElement", q8Args, 1.262185, 1.958231).thenCompose(result -> {
System.out.println(result); // >>> true
return asyncCommands.vemb("quantSetQ8", "quantElement");
}).thenCompose(result -> {
System.out.println("Q8: " + result); // >>> Q8: [1.2643694877624512, 1.958230972290039]
VAddArgs noQuantArgs = VAddArgs.Builder.quantizationType(QuantizationType.NO_QUANTIZATION);
return asyncCommands.vadd("quantSetNoQ", "quantElement", noQuantArgs, 1.262185, 1.958231);
}).thenCompose(result -> {
System.out.println(result); // >>> true
return asyncCommands.vemb("quantSetNoQ", "quantElement");
}).thenCompose(result -> {
System.out.println("NOQUANT: " + result); // >>> NOQUANT: [1.262184977531433, 1.958230972290039]
VAddArgs binArgs = VAddArgs.Builder.quantizationType(QuantizationType.BINARY);
return asyncCommands.vadd("quantSetBin", "quantElement", binArgs, 1.262185, 1.958231);
}).thenCompose(result -> {
System.out.println(result); // >>> true
return asyncCommands.vemb("quantSetBin", "quantElement");
}).thenAccept(result -> {
System.out.println("BIN: " + result); // >>> BIN: [1.0, 1.0]
}).toCompletableFuture();Java(响应式 - Lettuce)
java
VAddArgs q8Args = VAddArgs.Builder.quantizationType(QuantizationType.Q8);
Mono<Void> quantizationOperations = reactiveCommands.vadd("quantSetQ8", "quantElement", q8Args, 1.262185, 1.958231)
.doOnNext(result -> {
System.out.println(result); // >>> true
}).flatMap(result -> reactiveCommands.vemb("quantSetQ8", "quantElement").collectList()).doOnNext(result -> {
System.out.println("Q8: " + result); // >>> Q8: [1.2643694877624512, 1.958230972290039]
}).flatMap(result -> {
VAddArgs noQuantArgs = VAddArgs.Builder.quantizationType(QuantizationType.NO_QUANTIZATION);
return reactiveCommands.vadd("quantSetNoQ", "quantElement", noQuantArgs, 1.262185, 1.958231);
}).doOnNext(result -> {
System.out.println(result); // >>> true
}).flatMap(result -> reactiveCommands.vemb("quantSetNoQ", "quantElement").collectList())
.doOnNext(result -> {
System.out.println("NOQUANT: " + result); // >>> NOQUANT: [1.262184977531433, 1.958230972290039]
}).flatMap(result -> {
VAddArgs binArgs = VAddArgs.Builder.quantizationType(QuantizationType.BINARY);
return reactiveCommands.vadd("quantSetBin", "quantElement", binArgs, 1.262185, 1.958231);
}).doOnNext(result -> {
System.out.println(result); // >>> true
}).flatMap(result -> reactiveCommands.vemb("quantSetBin", "quantElement").collectList())
.doOnNext(result -> {
System.out.println("BIN: " + result); // >>> BIN: [1.0, 1.0]
}).then();删除性能
使用 DEL 删除大型向量集可能会导致延迟尖峰:
- Redis 必须取消链接并重新构建许多图节点。
- 删除数百万个元素时,延迟最为明显。
保存和加载性能
向量集保存和加载完整的 HNSW 图结构:
- 从磁盘重新加载很快,且无需重建图。
例如:包含 300 个分量的 300 万向量集加载时间约为 15 秒。
调优技巧总结
| 因素 | 对性能的影响 | 建议 |
|---|---|---|
EF | 查询变慢但召回率更高 | 从较低值(例如 200)开始,然后向上调整 |
M | 每个节点占用更多内存,召回率更高 | 除非召回率太低,否则使用默认值 |
| 量化类型 | 二进制最快,FP32 最慢 | 除非需要全精度,否则使用 Q8 或 BIN |
CAS | 使用多线程实现更快的插入 | 当需要高写入吞吐量时使用 |