内存优化
提示
来自deepseek解释
原文链接:https://redis.io/docs/latest/develop/data-types/vector-sets/memory/
代码示例说明
以下代码示例展示了如何使用不同的编程语言和客户端库执行相同的操作。为简洁起见,本译文仅保留 Java 相关的示例(同步 Jedis、异步 Lettuce、响应式 Lettuce)。
每个代码示例均以不同语言展示相同的基本操作。具体语法和模式会因语言和客户端库而异,但底层的 Redis 命令和行为保持一致。
概述
Redis 向量集合效率很高,但向量相似性索引和图遍历需要在内存使用上进行权衡。本指南通过量化、图参数调整和属性选择帮助您管理内存使用。
量化模式
向量集合支持三种量化级别:
| 模式 | 内存使用 | 召回率 | 说明 |
|---|---|---|---|
Q8 | 缩小 4 倍 | 高 | 默认,快速且准确 |
BIN | 缩小 32 倍 | 较低 | 最快,适用于粗粒度搜索 |
NOQUANT | 全尺寸 | 最高 | 最佳精度,最慢 |
除非您的用例需要超高精度(使用 NOQUANT)或超高效率(使用 BIN),否则请使用 Q8。
图结构内存
HNSW 图为每个节点存储多个连接。每个节点:
- 平均有
M * 2 + M * 0.33个指针(默认 M = 16)。 - 每个指针占用 8 字节。
- 每节点约分配 1.33 层。
当 M = 64 时,单个节点仅链接部分就可能消耗约 1 KB。
为减少内存:
- 降低
M以减少每节点的连接数。 - 除非需要提高召回率,否则避免使用过大的
M值。
属性和标签大小
每个节点存储:
- 一个字符串标签(元素名称)
- 可选的 JSON 属性字符串
技巧:
- 对标签使用短且长度固定的字符串。
- 保持属性 JSON 尽量简洁扁平。例如,使用
{"year":2020}而不是嵌套数据。
向量维度
高维向量会增加存储:
- 300 个分量,
FP32格式 = 每个向量 1200 字节 - 300 个分量,
Q8格式 = 每个向量 300 字节
您可以在 VADD 时使用 REDUCE 选项来降低维度,该选项应用随机投影:
难度: 高级
命令: VDIM
可用客户端: Redis CLI, C#, Go, Java(异步 - Lettuce), Java(响应式 - Lettuce), Java(同步 - Jedis), JavaScript(Node.js), PHP, Python, Rust(异步), Rust(同步)
Java(同步 - Jedis)
java
float[] values = new float[300];
for (int i = 0; i < 300; i++)
values[i] = i / 299.0f;
boolean res37 = jedis.vadd("setNotReduced", values, "element");
System.out.println(res37); // >>> true
long res38 = jedis.vdim("setNotReduced");
System.out.println(res38); // >>> 300
boolean res39 = jedis.vadd("setReduced", values, "element", 100, new VAddParams());
System.out.println(res39); // >>> true
long res40 = jedis.vdim("setReduced");
System.out.println(res40); // >>> 100Java(异步 - Lettuce)
java
// Create a list of 300 arbitrary values.
Double[] values = new Double[300];
for (int i = 0; i < 300; i++) {
values[i] = (double) i / 299;
}
CompletableFuture<Void> dimensionalityReductionOperations = asyncCommands.vadd("setNotReduced", "element", values)
.thenCompose(result -> {
System.out.println(result); // >>> true
return asyncCommands.vdim("setNotReduced");
}).thenCompose(result -> {
System.out.println(result); // >>> 300
return asyncCommands.vadd("setReduced", 100, "element", values);
}).thenCompose(result -> {
System.out.println(result); // >>> true
return asyncCommands.vdim("setReduced");
}).thenAccept(result -> {
System.out.println(result); // >>> 100
}).toCompletableFuture();Java(响应式 - Lettuce)
java
// Create a list of 300 arbitrary values.
Double[] values = new Double[300];
for (int i = 0; i < 300; i++) {
values[i] = (double) i / 299;
}
Mono<Void> dimensionalityReductionOperations = reactiveCommands.vadd("setNotReduced", "element", values)
.doOnNext(result -> {
System.out.println(result); // >>> true
}).flatMap(result -> reactiveCommands.vdim("setNotReduced")).doOnNext(result -> {
System.out.println(result); // >>> 300
}).flatMap(result -> reactiveCommands.vadd("setReduced", 100, "element", values)).doOnNext(result -> {
System.out.println(result); // >>> true
}).flatMap(result -> reactiveCommands.vdim("setReduced")).doOnNext(result -> {
System.out.println(result); // >>> 100
}).then();这会将一个 300 维向量投影到 100 维,从而减小体积并提高速度,但会牺牲部分召回率。
总结
| 策略 | 效果 |
|---|---|
使用 Q8 | 对大多数用例来说是最好的权衡 |
使用 BIN | 最小内存,最快搜索 |
降低 M | 缩小 HNSW 链接图大小 |
| 降低维度 | 减少每个向量的内存 |
| 最小化 JSON | 更小的属性,每个节点占用更少内存 |