Performance
Learn how Redis vector sets behave under load and how to optimize for speed and recall
Vector set is a new data type that is currently in preview and may be subject to change.
Query performance
Vector similarity queries using the VSIM
are threaded by default. Redis uses up to 32 threads to process these queries in parallel.
VSIM
performance scales nearly linearly with available CPU cores.- Expect ~50,000 similarity queries per second for a 3M-item set with 300-dim vectors using int8 quantization.
- Performance depends heavily on the
EF
parameter:- Higher
EF
improves recall, but slows down search. - Lower
EF
returns faster results with reduced accuracy.
- Higher
Insertion performance
Inserting vectors with the VADD
command is more computationally expensive than querying:
- Insertion is single-threaded by default.
- Use the
CAS
option to offload candidate graph search to a background thread. - Expect a few thousand insertions per second on a single node.
Quantization effects
Quantization greatly impacts both speed and memory:
Q8
(default): 4x smaller thanFP32
, high recall, high speedBIN
(binary): 32x smaller thanFP32
, lower recall, fastest searchNOQUANT
(FP32
): Full precision, slower performance, highest memory use
Use the quantization mode that best fits your tradeoff between precision and efficiency.
The examples below show how the different modes affect a simple vector.
Note that even with NOQUANT
mode, the values change slightly,
due to floating point rounding.
> VADD quantSetQ8 VALUES 2 1.262185 1.958231 quantElement Q8
(integer) 1
> VEMB quantSetQ8 quantElement
1) "1.2643694877624512"
2) "1.958230972290039"
> VADD quantSetNoQ VALUES 2 1.262185 1.958231 quantElement NOQUANT
(integer) 1
> VEMB quantSetNoQ quantElement
1) "1.262184977531433"
2) "1.958230972290039"
> VADD quantSetBin VALUES 2 1.262185 1.958231 quantElement BIN
(integer) 1
> VEMB quantSetBin quantElement
1) "1"
2) "1"
"""
Code samples for Vector set doc pages:
https://redis.io/docs/latest/develop/data-types/vector-sets/
"""
import redis
from redis.commands.vectorset.commands import (
QuantizationOptions
)
r = redis.Redis(decode_responses=True)
res1 = r.vset().vadd("points", [1.0, 1.0], "pt:A")
print(res1) # >>> 1
res2 = r.vset().vadd("points", [-1.0, -1.0], "pt:B")
print(res2) # >>> 1
res3 = r.vset().vadd("points", [-1.0, 1.0], "pt:C")
print(res3) # >>> 1
res4 = r.vset().vadd("points", [1.0, -1.0], "pt:D")
print(res4) # >>> 1
res5 = r.vset().vadd("points", [1.0, 0], "pt:E")
print(res5) # >>> 1
res6 = r.type("points")
print(res6) # >>> vectorset
res7 = r.vset().vcard("points")
print(res7) # >>> 5
res8 = r.vset().vdim("points")
print(res8) # >>> 2
res9 = r.vset().vemb("points", "pt:A")
print(res9) # >>> [0.9999999403953552, 0.9999999403953552]
res10 = r.vset().vemb("points", "pt:B")
print(res10) # >>> [-0.9999999403953552, -0.9999999403953552]
res11 = r.vset().vemb("points", "pt:C")
print(res11) # >>> [-0.9999999403953552, 0.9999999403953552]
res12 = r.vset().vemb("points", "pt:D")
print(res12) # >>> [0.9999999403953552, -0.9999999403953552]
res13 = r.vset().vemb("points", "pt:E")
print(res13) # >>> [1, 0]
res14 = r.vset().vsetattr("points", "pt:A", {
"name": "Point A",
"description": "First point added"
})
print(res14) # >>> 1
res15 = r.vset().vgetattr("points", "pt:A")
print(res15)
# >>> {'name': 'Point A', 'description': 'First point added'}
res16 = r.vset().vsetattr("points", "pt:A", "")
print(res16) # >>> 1
res17 = r.vset().vgetattr("points", "pt:A")
print(res17) # >>> None
res18 = r.vset().vadd("points", [0, 0], "pt:F")
print(res18) # >>> 1
res19 = r.vset().vcard("points")
print(res19) # >>> 6
res20 = r.vset().vrem("points", "pt:F")
print(res20) # >>> 1
res21 = r.vset().vcard("points")
print(res21) # >>> 5
res22 = r.vset().vsim("points", [0.9, 0.1])
print(res22)
# >>> ['pt:E', 'pt:A', 'pt:D', 'pt:C', 'pt:B']
res23 = r.vset().vsim(
"points", "pt:A",
with_scores=True,
count=4
)
print(res23)
# >>> {'pt:A': 1.0, 'pt:E': 0.8535534143447876, 'pt:D': 0.5, 'pt:C': 0.5}
res24 = r.vset().vsetattr("points", "pt:A", {
"size": "large",
"price": 18.99
})
print(res24) # >>> 1
res25 = r.vset().vsetattr("points", "pt:B", {
"size": "large",
"price": 35.99
})
print(res25) # >>> 1
res26 = r.vset().vsetattr("points", "pt:C", {
"size": "large",
"price": 25.99
})
print(res26) # >>> 1
res27 = r.vset().vsetattr("points", "pt:D", {
"size": "small",
"price": 21.00
})
print(res27) # >>> 1
res28 = r.vset().vsetattr("points", "pt:E", {
"size": "small",
"price": 17.75
})
print(res28) # >>> 1
# Return elements in order of distance from point A whose
# `size` attribute is `large`.
res29 = r.vset().vsim(
"points", "pt:A",
filter='.size == "large"'
)
print(res29) # >>> ['pt:A', 'pt:C', 'pt:B']
# Return elements in order of distance from point A whose size is
# `large` and whose price is greater than 20.00.
res30 = r.vset().vsim(
"points", "pt:A",
filter='.size == "large" && .price > 20.00'
)
print(res30) # >>> ['pt:C', 'pt:B']
# Import `QuantizationOptions` enum using:
#
# from redis.commands.vectorset.commands import (
# QuantizationOptions
# )
res31 = r.vset().vadd(
"quantSetQ8", [1.262185, 1.958231],
"quantElement",
quantization=QuantizationOptions.Q8
)
print(res31) # >>> 1
res32 = r.vset().vemb("quantSetQ8", "quantElement")
print(f"Q8: {res32}")
# >>> Q8: [1.2643694877624512, 1.958230972290039]
res33 = r.vset().vadd(
"quantSetNoQ", [1.262185, 1.958231],
"quantElement",
quantization=QuantizationOptions.NOQUANT
)
print(res33) # >>> 1
res34 = r.vset().vemb("quantSetNoQ", "quantElement")
print(f"NOQUANT: {res34}")
# >>> NOQUANT: [1.262184977531433, 1.958230972290039]
res35 = r.vset().vadd(
"quantSetBin", [1.262185, 1.958231],
"quantElement",
quantization=QuantizationOptions.BIN
)
print(res35) # >>> 1
res36 = r.vset().vemb("quantSetBin", "quantElement")
print(f"BIN: {res36}")
# >>> BIN: [1, 1]
# Create a list of 300 arbitrary values.
values = [x / 299 for x in range(300)]
res37 = r.vset().vadd(
"setNotReduced",
values,
"element"
)
print(res37) # >>> 1
res38 = r.vset().vdim("setNotReduced")
print(res38) # >>> 300
res39 = r.vset().vadd(
"setReduced",
values,
"element",
reduce_dim=100
)
print(res39) # >>> 1
res40 = r.vset().vdim("setReduced") # >>> 100
print(res40)
package io.redis.examples.async;
import io.lettuce.core.*;
import io.lettuce.core.api.async.RedisAsyncCommands;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.vector.QuantizationType;
import java.util.concurrent.CompletableFuture;
public class VectorSetExample {
public void run() {
RedisClient redisClient = RedisClient.create("redis://localhost:6379");
try (StatefulRedisConnection<String, String> connection = redisClient.connect()) {
RedisAsyncCommands<String, String> asyncCommands = connection.async();
CompletableFuture<Boolean> addPointA = asyncCommands.vadd("points", "pt:A", 1.0, 1.0).thenApply(result -> {
System.out.println(result); // >>> true
return result;
}).toCompletableFuture();
CompletableFuture<Boolean> addPointB = asyncCommands.vadd("points", "pt:B", -1.0, -1.0).thenApply(result -> {
System.out.println(result); // >>> true
return result;
}).toCompletableFuture();
CompletableFuture<Boolean> addPointC = asyncCommands.vadd("points", "pt:C", -1.0, 1.0).thenApply(result -> {
System.out.println(result); // >>> true
return result;
}).toCompletableFuture();
CompletableFuture<Boolean> addPointD = asyncCommands.vadd("points", "pt:D", 1.0, -1.0).thenApply(result -> {
System.out.println(result); // >>> true
return result;
}).toCompletableFuture();
CompletableFuture<Boolean> addPointE = asyncCommands.vadd("points", "pt:E", 1.0, 0.0).thenApply(result -> {
System.out.println(result); // >>> true
return result;
}).toCompletableFuture();
// Chain checkDataType after all vadd operations complete
CompletableFuture<Void> vaddOperations = CompletableFuture
.allOf(addPointA, addPointB, addPointC, addPointD, addPointE)
.thenCompose(ignored -> asyncCommands.type("points")).thenAccept(result -> {
System.out.println(result); // >>> vectorset
}).toCompletableFuture();
CompletableFuture<Void> getCardinality = asyncCommands.vcard("points").thenAccept(result -> {
System.out.println(result); // >>> 5
}).toCompletableFuture();
CompletableFuture<Void> getDimensions = asyncCommands.vdim("points").thenAccept(result -> {
System.out.println(result); // >>> 2
}).toCompletableFuture();
CompletableFuture<Void> getEmbeddingA = asyncCommands.vemb("points", "pt:A").thenAccept(result -> {
System.out.println(result); // >>> [0.9999999403953552, 0.9999999403953552]
}).toCompletableFuture();
CompletableFuture<Void> getEmbeddingB = asyncCommands.vemb("points", "pt:B").thenAccept(result -> {
System.out.println(result); // >>> [-0.9999999403953552, -0.9999999403953552]
}).toCompletableFuture();
CompletableFuture<Void> getEmbeddingC = asyncCommands.vemb("points", "pt:C").thenAccept(result -> {
System.out.println(result); // >>> [-0.9999999403953552, 0.9999999403953552]
}).toCompletableFuture();
CompletableFuture<Void> getEmbeddingD = asyncCommands.vemb("points", "pt:D").thenAccept(result -> {
System.out.println(result); // >>> [0.9999999403953552, -0.9999999403953552]
}).toCompletableFuture();
CompletableFuture<Void> getEmbeddingE = asyncCommands.vemb("points", "pt:E").thenAccept(result -> {
System.out.println(result); // >>> [1.0, 0.0]
}).toCompletableFuture();
CompletableFuture<Void> setAttributeA = asyncCommands
.vsetattr("points", "pt:A", "{\"name\": \"Point A\", \"description\": \"First point added\"}")
.thenAccept(result -> {
System.out.println(result); // >>> true
}).toCompletableFuture();
CompletableFuture<Void> getAttributeA = asyncCommands.vgetattr("points", "pt:A").thenAccept(result -> {
System.out.println(result); // >>> {"name": "Point A", "description": "First point added"}
}).toCompletableFuture();
CompletableFuture<Void> clearAttributeA = asyncCommands.vsetattr("points", "pt:A", "").thenAccept(result -> {
System.out.println(result); // >>> true
}).toCompletableFuture();
CompletableFuture<Void> verifyAttributeCleared = asyncCommands.vgetattr("points", "pt:A").thenAccept(result -> {
System.out.println(result); // >>> null
}).toCompletableFuture();
CompletableFuture<Void> addTempPointF = asyncCommands.vadd("points", "pt:F", 0.0, 0.0).thenAccept(result -> {
System.out.println(result); // >>> true
}).toCompletableFuture();
CompletableFuture<Void> checkCardinalityBefore = asyncCommands.vcard("points").thenAccept(result -> {
System.out.println(result); // >>> 6
}).toCompletableFuture();
CompletableFuture<Void> removePointF = asyncCommands.vrem("points", "pt:F").thenAccept(result -> {
System.out.println(result); // >>> true
}).toCompletableFuture();
CompletableFuture<Void> checkCardinalityAfter = asyncCommands.vcard("points").thenAccept(result -> {
System.out.println(result); // >>> 5
}).toCompletableFuture();
CompletableFuture<Void> basicSimilaritySearch = asyncCommands.vsim("points", 0.9, 0.1).thenAccept(result -> {
System.out.println(result); // >>> [pt:E, pt:A, pt:D, pt:C, pt:B]
}).toCompletableFuture();
VSimArgs vsimArgs = new VSimArgs();
vsimArgs.count(4L);
CompletableFuture<Void> similaritySearchWithScore = asyncCommands.vsimWithScore("points", vsimArgs, "pt:A")
.thenAccept(result -> {
System.out.println(result); // >>> {pt:A=1.0, pt:E=0.8535534143447876, pt:D=0.5, pt:C=0.5}
}).toCompletableFuture();
CompletableFuture<Void> filteredSimilaritySearch = asyncCommands
.vsetattr("points", "pt:A", "{\"size\": \"large\", \"price\": 18.99}").thenCompose(result -> {
System.out.println(result); // >>> true
return asyncCommands.vsetattr("points", "pt:B", "{\"size\": \"large\", \"price\": 35.99}");
}).thenCompose(result -> {
System.out.println(result); // >>> true
return asyncCommands.vsetattr("points", "pt:C", "{\"size\": \"large\", \"price\": 25.99}");
}).thenCompose(result -> {
System.out.println(result); // >>> true
return asyncCommands.vsetattr("points", "pt:D", "{\"size\": \"small\", \"price\": 21.00}");
}).thenCompose(result -> {
System.out.println(result); // >>> true
return asyncCommands.vsetattr("points", "pt:E", "{\"size\": \"small\", \"price\": 17.75}");
}).thenCompose(result -> {
System.out.println(result); // >>> true
// Return elements in order of distance from point A whose size attribute is large.
VSimArgs filterArgs = new VSimArgs();
filterArgs.filter(".size == \"large\"");
return asyncCommands.vsim("points", filterArgs, "pt:A");
}).thenCompose(result -> {
System.out.println(result); // >>> [pt:A, pt:C, pt:B]
// Return elements in order of distance from point A whose size is large and price > 20.00.
VSimArgs filterArgs2 = new VSimArgs();
filterArgs2.filter(".size == \"large\" && .price > 20.00");
return asyncCommands.vsim("points", filterArgs2, "pt:A");
}).thenAccept(result -> {
System.out.println(result); // >>> [pt:C, pt:B]
}).toCompletableFuture();
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();
// 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();
// Wait for all async operations to complete
CompletableFuture.allOf(
// Vector addition operations (chained: parallel vadd + sequential checkDataType)
vaddOperations,
// Cardinality and dimension operations
getCardinality, getDimensions,
// Vector embedding retrieval operations
getEmbeddingA, getEmbeddingB, getEmbeddingC, getEmbeddingD, getEmbeddingE,
// Attribute operations
setAttributeA, getAttributeA, clearAttributeA, verifyAttributeCleared,
// Vector removal operations
addTempPointF, checkCardinalityBefore, removePointF, checkCardinalityAfter,
// Similarity search operations
basicSimilaritySearch, similaritySearchWithScore, filteredSimilaritySearch,
// Advanced operations
quantizationOperations, dimensionalityReductionOperations).join();
} finally {
redisClient.shutdown();
}
}
}
package io.redis.examples.reactive;
import io.lettuce.core.*;
import io.lettuce.core.api.reactive.RedisReactiveCommands;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.vector.QuantizationType;
import reactor.core.publisher.Mono;
public class VectorSetExample {
public void run() {
RedisClient redisClient = RedisClient.create("redis://localhost:6379");
try (StatefulRedisConnection<String, String> connection = redisClient.connect()) {
RedisReactiveCommands<String, String> reactiveCommands = connection.reactive();
Mono<Boolean> addPointA = reactiveCommands.vadd("points", "pt:A", 1.0, 1.0).doOnNext(result -> {
System.out.println(result); // >>> true
});
Mono<Boolean> addPointB = reactiveCommands.vadd("points", "pt:B", -1.0, -1.0).doOnNext(result -> {
System.out.println(result); // >>> true
});
Mono<Boolean> addPointC = reactiveCommands.vadd("points", "pt:C", -1.0, 1.0).doOnNext(result -> {
System.out.println(result); // >>> true
});
Mono<Boolean> addPointD = reactiveCommands.vadd("points", "pt:D", 1.0, -1.0).doOnNext(result -> {
System.out.println(result); // >>> true
});
Mono<Boolean> addPointE = reactiveCommands.vadd("points", "pt:E", 1.0, 0.0).doOnNext(result -> {
System.out.println(result); // >>> true
});
Mono<Void> vaddOperations = Mono.when(addPointA, addPointB, addPointC, addPointD, addPointE)
.then(reactiveCommands.type("points")).doOnNext(result -> {
System.out.println(result); // >>> vectorset
}).then();
Mono<Long> getCardinality = reactiveCommands.vcard("points").doOnNext(result -> {
System.out.println(result); // >>> 5
});
Mono<Long> getDimensions = reactiveCommands.vdim("points").doOnNext(result -> {
System.out.println(result); // >>> 2
});
Mono<java.util.List<Double>> getEmbeddingA = reactiveCommands.vemb("points", "pt:A").collectList()
.doOnNext(result -> {
System.out.println(result); // >>> [0.9999999403953552, 0.9999999403953552]
});
Mono<java.util.List<Double>> getEmbeddingB = reactiveCommands.vemb("points", "pt:B").collectList()
.doOnNext(result -> {
System.out.println(result); // >>> [-0.9999999403953552, -0.9999999403953552]
});
Mono<java.util.List<Double>> getEmbeddingC = reactiveCommands.vemb("points", "pt:C").collectList()
.doOnNext(result -> {
System.out.println(result); // >>> [-0.9999999403953552, 0.9999999403953552]
});
Mono<java.util.List<Double>> getEmbeddingD = reactiveCommands.vemb("points", "pt:D").collectList()
.doOnNext(result -> {
System.out.println(result); // >>> [0.9999999403953552, -0.9999999403953552]
});
Mono<java.util.List<Double>> getEmbeddingE = reactiveCommands.vemb("points", "pt:E").collectList()
.doOnNext(result -> {
System.out.println(result); // >>> [1.0, 0.0]
});
Mono<Boolean> setAttributeA = reactiveCommands
.vsetattr("points", "pt:A", "{\"name\": \"Point A\", \"description\": \"First point added\"}")
.doOnNext(result -> {
System.out.println(result); // >>> true
});
Mono<String> getAttributeA = reactiveCommands.vgetattr("points", "pt:A").doOnNext(result -> {
System.out.println(result); // >>> {"name": "Point A", "description": "First point added"}
});
Mono<Boolean> clearAttributeA = reactiveCommands.vsetattr("points", "pt:A", "").doOnNext(result -> {
System.out.println(result); // >>> true
});
Mono<String> verifyAttributeCleared = reactiveCommands.vgetattr("points", "pt:A").doOnNext(result -> {
System.out.println(result); // >>> null
});
Mono<Boolean> addTempPointF = reactiveCommands.vadd("points", "pt:F", 0.0, 0.0).doOnNext(result -> {
System.out.println(result); // >>> true
});
Mono<Long> checkCardinalityBefore = reactiveCommands.vcard("points").doOnNext(result -> {
System.out.println(result); // >>> 6
});
Mono<Boolean> removePointF = reactiveCommands.vrem("points", "pt:F").doOnNext(result -> {
System.out.println(result); // >>> true
});
Mono<Long> checkCardinalityAfter = reactiveCommands.vcard("points").doOnNext(result -> {
System.out.println(result); // >>> 5
});
Mono<java.util.List<String>> basicSimilaritySearch = reactiveCommands.vsim("points", 0.9, 0.1).collectList()
.doOnNext(result -> {
System.out.println(result); // >>> [pt:E, pt:A, pt:D, pt:C, pt:B]
});
VSimArgs vsimArgs = new VSimArgs();
vsimArgs.count(4L);
Mono<java.util.Map<String, Double>> similaritySearchWithScore = reactiveCommands
.vsimWithScore("points", vsimArgs, "pt:A").doOnNext(result -> {
System.out.println(result); // >>> {pt:A=1.0, pt:E=0.8535534143447876, pt:D=0.5, pt:C=0.5}
});
Mono<Void> filteredSimilaritySearch = reactiveCommands
.vsetattr("points", "pt:A", "{\"size\": \"large\", \"price\": 18.99}").doOnNext(result -> {
System.out.println(result); // >>> true
}).flatMap(result -> reactiveCommands.vsetattr("points", "pt:B", "{\"size\": \"large\", \"price\": 35.99}"))
.doOnNext(result -> {
System.out.println(result); // >>> true
}).flatMap(result -> reactiveCommands.vsetattr("points", "pt:C", "{\"size\": \"large\", \"price\": 25.99}"))
.doOnNext(result -> {
System.out.println(result); // >>> true
}).flatMap(result -> reactiveCommands.vsetattr("points", "pt:D", "{\"size\": \"small\", \"price\": 21.00}"))
.doOnNext(result -> {
System.out.println(result); // >>> true
}).flatMap(result -> reactiveCommands.vsetattr("points", "pt:E", "{\"size\": \"small\", \"price\": 17.75}"))
.doOnNext(result -> {
System.out.println(result); // >>> true
}).flatMap(result -> {
// Return elements in order of distance from point A whose size attribute is large.
VSimArgs filterArgs = new VSimArgs();
filterArgs.filter(".size == \"large\"");
return reactiveCommands.vsim("points", filterArgs, "pt:A").collectList();
}).doOnNext(result -> {
System.out.println(result); // >>> [pt:A, pt:C, pt:B]
}).flatMap(result -> {
// Return elements in order of distance from point A whose size is large and price > 20.00.
VSimArgs filterArgs2 = new VSimArgs();
filterArgs2.filter(".size == \"large\" && .price > 20.00");
return reactiveCommands.vsim("points", filterArgs2, "pt:A").collectList();
}).doOnNext(result -> {
System.out.println(result); // >>> [pt:C, pt:B]
}).then();
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();
// 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();
// Wait for all reactive operations to complete
Mono.when(
// Vector addition operations (chained sequentially)
vaddOperations,
// Cardinality and dimension operations
getCardinality, getDimensions,
// Vector embedding retrieval operations
getEmbeddingA, getEmbeddingB, getEmbeddingC, getEmbeddingD, getEmbeddingE,
// Attribute operations
setAttributeA, getAttributeA, clearAttributeA, verifyAttributeCleared,
// Vector removal operations
addTempPointF, checkCardinalityBefore, removePointF, checkCardinalityAfter,
// Similarity search operations
basicSimilaritySearch, similaritySearchWithScore, filteredSimilaritySearch,
// Advanced operations
quantizationOperations, dimensionalityReductionOperations).block();
} finally {
redisClient.shutdown();
}
}
}
package example_commands_test
import (
"context"
"fmt"
"sort"
"github.com/redis/go-redis/v9"
)
func ExampleClient_vectorset() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password set
DB: 0, // use default DB
})
defer rdb.Close()
res1, err := rdb.VAdd(ctx, "points", "pt:A",
&redis.VectorValues{Val: []float64{1.0, 1.0}},
).Result()
if err != nil {
panic(err)
}
fmt.Println(res1) // >>> true
res2, err := rdb.VAdd(ctx, "points", "pt:B",
&redis.VectorValues{Val: []float64{-1.0, -1.0}},
).Result()
if err != nil {
panic(err)
}
fmt.Println(res2) // >>> true
res3, err := rdb.VAdd(ctx, "points", "pt:C",
&redis.VectorValues{Val: []float64{-1.0, 1.0}},
).Result()
if err != nil {
panic(err)
}
fmt.Println(res3) // >>> true
res4, err := rdb.VAdd(ctx, "points", "pt:D",
&redis.VectorValues{Val: []float64{1.0, -1.0}},
).Result()
if err != nil {
panic(err)
}
fmt.Println(res4) // >>> true
res5, err := rdb.VAdd(ctx, "points", "pt:E",
&redis.VectorValues{Val: []float64{1.0, 0.0}},
).Result()
if err != nil {
panic(err)
}
fmt.Println(res5) // >>> true
res6, err := rdb.Type(ctx, "points").Result()
if err != nil {
panic(err)
}
fmt.Println(res6) // >>> vectorset
res7, err := rdb.VCard(ctx, "points").Result()
if err != nil {
panic(err)
}
fmt.Println(res7) // >>> 5
res8, err := rdb.VDim(ctx, "points").Result()
if err != nil {
panic(err)
}
fmt.Println(res8) // >>> 2
res9, err := rdb.VEmb(ctx, "points", "pt:A", false).Result()
if err != nil {
panic(err)
}
fmt.Println(res9) // >>> [0.9999999403953552 0.9999999403953552]
res10, err := rdb.VEmb(ctx, "points", "pt:B", false).Result()
if err != nil {
panic(err)
}
fmt.Println(res10) // >>> [-0.9999999403953552 -0.9999999403953552]
res11, err := rdb.VEmb(ctx, "points", "pt:C", false).Result()
if err != nil {
panic(err)
}
fmt.Println(res11) // >>> [-0.9999999403953552 0.9999999403953552]
res12, err := rdb.VEmb(ctx, "points", "pt:D", false).Result()
if err != nil {
panic(err)
}
fmt.Println(res12) // >>> [0.9999999403953552 -0.9999999403953552]
res13, err := rdb.VEmb(ctx, "points", "pt:E", false).Result()
if err != nil {
panic(err)
}
fmt.Println(res13) // >>> [1 0]
attrs := map[string]interface{}{
"name": "Point A",
"description": "First point added",
}
res14, err := rdb.VSetAttr(ctx, "points", "pt:A", attrs).Result()
if err != nil {
panic(err)
}
fmt.Println(res14) // >>> true
res15, err := rdb.VGetAttr(ctx, "points", "pt:A").Result()
if err != nil {
panic(err)
}
fmt.Println(res15)
// >>> {"description":"First point added","name":"Point A"}
res16, err := rdb.VClearAttributes(ctx, "points", "pt:A").Result()
if err != nil {
panic(err)
}
fmt.Println(res16) // >>> true
// `VGetAttr()` returns an error if the attribute doesn't exist.
_, err = rdb.VGetAttr(ctx, "points", "pt:A").Result()
if err != nil {
fmt.Println(err)
}
res18, err := rdb.VAdd(ctx, "points", "pt:F",
&redis.VectorValues{Val: []float64{0.0, 0.0}},
).Result()
if err != nil {
panic(err)
}
fmt.Println(res18) // >>> true
res19, err := rdb.VCard(ctx, "points").Result()
if err != nil {
panic(err)
}
fmt.Println(res19) // >>> 6
res20, err := rdb.VRem(ctx, "points", "pt:F").Result()
if err != nil {
panic(err)
}
fmt.Println(res20) // >>> true
res21, err := rdb.VCard(ctx, "points").Result()
if err != nil {
panic(err)
}
fmt.Println(res21) // >>> 5
res22, err := rdb.VSim(ctx, "points",
&redis.VectorValues{Val: []float64{0.9, 0.1}},
).Result()
if err != nil {
panic(err)
}
fmt.Println(res22) // >>> [pt:E pt:A pt:D pt:C pt:B]
res23, err := rdb.VSimWithArgsWithScores(
ctx,
"points",
&redis.VectorRef{Name: "pt:A"},
&redis.VSimArgs{Count: 4},
).Result()
if err != nil {
panic(err)
}
sort.Slice(res23, func(i, j int) bool {
return res23[i].Name < res23[j].Name
})
fmt.Println(res23)
// >>> [{pt:A 1} {pt:C 0.5} {pt:D 0.5} {pt:E 0.8535534143447876}]
// Set attributes for filtering
res24, err := rdb.VSetAttr(ctx, "points", "pt:A",
map[string]interface{}{
"size": "large",
"price": 18.99,
},
).Result()
if err != nil {
panic(err)
}
fmt.Println(res24) // >>> true
res25, err := rdb.VSetAttr(ctx, "points", "pt:B",
map[string]interface{}{
"size": "large",
"price": 35.99,
},
).Result()
if err != nil {
panic(err)
}
fmt.Println(res25) // >>> true
res26, err := rdb.VSetAttr(ctx, "points", "pt:C",
map[string]interface{}{
"size": "large",
"price": 25.99,
},
).Result()
if err != nil {
panic(err)
}
fmt.Println(res26) // >>> true
res27, err := rdb.VSetAttr(ctx, "points", "pt:D",
map[string]interface{}{
"size": "small",
"price": 21.00,
},
).Result()
if err != nil {
panic(err)
}
fmt.Println(res27) // >>> true
res28, err := rdb.VSetAttr(ctx, "points", "pt:E",
map[string]interface{}{
"size": "small",
"price": 17.75,
},
).Result()
if err != nil {
panic(err)
}
fmt.Println(res28) // >>> true
// Return elements in order of distance from point A whose
// `size` attribute is `large`.
res29, err := rdb.VSimWithArgs(ctx, "points",
&redis.VectorRef{Name: "pt:A"},
&redis.VSimArgs{Filter: `.size == "large"`},
).Result()
if err != nil {
panic(err)
}
fmt.Println(res29) // >>> [pt:A pt:C pt:B]
// Return elements in order of distance from point A whose size is
// `large` and whose price is greater than 20.00.
res30, err := rdb.VSimWithArgs(ctx, "points",
&redis.VectorRef{Name: "pt:A"},
&redis.VSimArgs{Filter: `.size == "large" && .price > 20.00`},
).Result()
if err != nil {
panic(err)
}
fmt.Println(res30) // >>> [pt:C pt:B]
}
func ExampleClient_vectorset_quantization() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password set
DB: 0, // use default DB
})
defer rdb.Close()
// Add with Q8 quantization
vecQ := &redis.VectorValues{Val: []float64{1.262185, 1.958231}}
res1, err := rdb.VAddWithArgs(ctx, "quantSetQ8", "quantElement", vecQ,
&redis.VAddArgs{
Q8: true,
},
).Result()
if err != nil {
panic(err)
}
fmt.Println(res1) // >>> true
embQ8, err := rdb.VEmb(ctx, "quantSetQ8", "quantElement", false).Result()
if err != nil {
panic(err)
}
fmt.Printf("Q8 embedding: %v\n", embQ8)
// >>> Q8 embedding: [1.2621850967407227 1.9582309722900391]
// Add with NOQUANT option
res2, err := rdb.VAddWithArgs(ctx, "quantSetNoQ", "quantElement", vecQ,
&redis.VAddArgs{
NoQuant: true,
},
).Result()
if err != nil {
panic(err)
}
fmt.Println(res2) // >>> true
embNoQ, err := rdb.VEmb(ctx, "quantSetNoQ", "quantElement", false).Result()
if err != nil {
panic(err)
}
fmt.Printf("NOQUANT embedding: %v\n", embNoQ)
// >>> NOQUANT embedding: [1.262185 1.958231]
// Add with BIN quantization
res3, err := rdb.VAddWithArgs(ctx, "quantSetBin", "quantElement", vecQ,
&redis.VAddArgs{
Bin: true,
},
).Result()
if err != nil {
panic(err)
}
fmt.Println(res3) // >>> true
embBin, err := rdb.VEmb(ctx, "quantSetBin", "quantElement", false).Result()
if err != nil {
panic(err)
}
fmt.Printf("BIN embedding: %v\n", embBin)
// >>> BIN embedding: [1 1]
}
func ExampleClient_vectorset_dimension_reduction() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password set
DB: 0, // use default DB
})
defer rdb.Close()
// Create a vector with 300 dimensions
values := make([]float64, 300)
for i := 0; i < 300; i++ {
values[i] = float64(i) / 299
}
vecLarge := &redis.VectorValues{Val: values}
// Add without reduction
res1, err := rdb.VAdd(ctx, "setNotReduced", "element", vecLarge).Result()
if err != nil {
panic(err)
}
fmt.Println(res1) // >>> true
dim1, err := rdb.VDim(ctx, "setNotReduced").Result()
if err != nil {
panic(err)
}
fmt.Printf("Dimension without reduction: %d\n", dim1)
// >>> Dimension without reduction: 300
// Add with reduction to 100 dimensions
res2, err := rdb.VAddWithArgs(ctx, "setReduced", "element", vecLarge,
&redis.VAddArgs{
Reduce: 100,
},
).Result()
if err != nil {
panic(err)
}
fmt.Println(res2) // >>> true
dim2, err := rdb.VDim(ctx, "setReduced").Result()
if err != nil {
panic(err)
}
fmt.Printf("Dimension after reduction: %d\n", dim2)
// >>> Dimension after reduction: 100
}
Deletion performance
Deleting large vector sets using the DEL
can cause latency spikes:
- Redis must unlink and restructure many graph nodes.
- Latency is most noticeable when deleting millions of elements.
Save and load performance
Vector sets save and load the full HNSW graph structure:
- When reloading from disk is fast and there's no need to rebuild the graph.
Example: A 3M vector set with 300 components loads in ~15 seconds.
Summary of tuning tips
Factor | Effect on performance | Tip |
---|---|---|
EF |
Slower queries but higher recall | Start low (for example, 200) and tune upward |
M |
More memory per node, better recall | Use defaults unless recall is too low |
Quant type | Binary is fastest, FP32 is slowest |
Use Q8 or BIN unless full precision needed |
CAS |
Faster insertions with threading | Use when high write throughput is needed |