Milvus
Milvus 开源向量相似度检索引擎,向量数据库
https://github.com/milvus-io/milvus
向量索引
https://milvus.io/cn/docs/v2.0.x/index.md
FLAT 无索引100%召回
FLAT 是指对向量进行原始文件存储,是唯一可以保证精确的检索结果的索引。FLAT 的结果也可以用于对照其他召回率低于 100% 的索引产生的结果。
对于每个查询,目标输入都要与数据集中的每个向量进行比较。
FLAT 是对向量的暴力搜索(brute-force search),速度最慢,但召回率最高(100%),磁盘空间占用最小。
IVF_FLAT 聚类
IVF_FLAT 索引通过聚类方法把空间里的点划分至 nlist 个单元,然后比较目标向量与所有单元中心的距离,选出 nprobe 个最近单元。然后比较这些被选中单元里的所有向量,得到最终的结果,极大地缩短了查询时间。
IVF_FLAT 索引不会对原始向量进行压缩,IVF_FLAT 索引文件的大小与原始数据文件大小相当。
建索引参数
nlist 聚类单元数,取值范围 [1, 65536]
查询参数
nq 输入向量个数
nprobe 查询取的单元数,取值范围 [1, 65536]
IVF_SQ8 聚类+压缩
IVF_SQ8 索引在 IVF_FLAT 基础上进行向量压缩。
IVF_SQ8 通过对向量进行标量量化(scalar quantization),能把原始向量中每个FLOAT(4 字节)转为UINT8(1 字节),从而可以把磁盘及内存、显存资源的消耗量减少为原来的 1/4 至 1/3。同样以 sift-1b 数据集为例,生成的 IVF_SQ8 索引文件只有 140 GB。
性能测试报告
https://milvus.io/docs/benchmark.md
100 万 128 维 SIFT 向量,建立 IVF_FLAT 索引,nlist=2048,1比n检索返回top1,入参 nprobe=16,并发检索,可达 440 qps、120 ms 平响
5000 万 128 维 SIFT 向量,建立 IVF_FLAT 索引,nlist=4096,1比n检索返回top1,入参 nprobe=16,并发检索,10个 query nodes 可达 90qps,216 ms 平响
百万512维,falt,(16c16g)*2,tp99 200ms,240qps
千万512维,flat,(16c32g)*2,tp99 2s,20qps
https://www.51cto.com/article/740699.html
距离计算方式
public enum MetricType {
INVALID,
// Only for float vectors
L2,
IP,
COSINE,
// Only for binary vectors
HAMMING,
JACCARD,
;
}
Milvus 使用示例
https://milvus.io/docs/manage-collections.md
https://milvus.io/docs/single-vector-search.md
@Test
public void test() {
MilvusClientV2 client = new MilvusClientV2(ConnectConfig.builder().uri("http://localhost:19530").build());
// 1.1、创建schema
CreateCollectionReq.CollectionSchema schema = client.createSchema();
// 1.2、向schema中添加字段
schema.addField(AddFieldReq.builder()
.fieldName("my_id")
.dataType(DataType.Int64)
.isPrimaryKey(true)
.autoID(false)
.build());
schema.addField(AddFieldReq.builder()
.fieldName("my_vector")
.dataType(DataType.FloatVector)
.dimension(1024)
.build());
schema.addField(AddFieldReq.builder()
.fieldName("my_vector2")
.dataType(DataType.FloatVector)
.dimension(1024)
.build());
// 2、创建collection
// 可以同步创建collection和索引,创建后collection就是已加载状态。
// 也可以分别创建collection和索引,创建后collection是未加载状态
CreateCollectionReq customizedSetupReq1 = CreateCollectionReq.builder()
.collectionName("customized_setup_1")
.collectionSchema(schema)
.numPartitions(3)
.build();
client.createCollection(customizedSetupReq1);
// 3、创建索引
IndexParam indexParamForIdField = IndexParam.builder()
.fieldName("my_id")
.indexType(IndexParam.IndexType.STL_SORT)
.build();
IndexParam indexParamForVectorField = IndexParam.builder()
.fieldName("my_vector")
.indexType(IndexParam.IndexType.FLAT)
.metricType(IndexParam.MetricType.COSINE)
.extraParams(Map.of("nlist", 1024))
.build();
IndexParam indexParamForVectorField2 = IndexParam.builder()
.fieldName("my_vector2")
.indexType(IndexParam.IndexType.FLAT)
.metricType(IndexParam.MetricType.COSINE)
.extraParams(Map.of("nlist", 1024))
.build();
List<IndexParam> indexParams = List.of(
indexParamForIdField,
indexParamForVectorField,
indexParamForVectorField2
);
CreateIndexReq createIndexReq = CreateIndexReq.builder()
.collectionName("customized_setup_1")
.indexParams(indexParams)
.build();
client.createIndex(createIndexReq);
// 4、加载collection到内存
LoadCollectionReq loadCollectionReq = LoadCollectionReq.builder()
.collectionName("customized_setup_1")
.build();
client.loadCollection(loadCollectionReq);
// 获取collection的加载状态
Boolean res = client.getLoadState(GetLoadStateReq.builder().collectionName("customized_setup_1").build());
System.out.println("customized_setup_1 已加载: " + res);
// 5、插入数据
List<JSONObject> data = Arrays.asList(
new JSONObject(Map.of("my_id", 0L, "my_vector", Arrays.asList(0.3580376395471989f, -0.6023495712049978f, 0.18414012509913835f, -0.26286205330961354f, 0.9029438446296592f), "color", "pink_8682")),
new JSONObject(Map.of("my_id", 1L, "my_vector", Arrays.asList(0.19886812562848388f, 0.06023560599112088f, 0.6976963061752597f, 0.2614474506242501f, 0.838729485096104f), "color", "red_7025")),
new JSONObject(Map.of("my_id", 2L, "my_vector", Arrays.asList(0.5718280481994695f, 0.24070317428066512f, -0.3737913482606834f, -0.06726932177492717f, -0.6980531615588608f), "color", "purple_4976"))
);
InsertReq insertReq = InsertReq.builder()
.collectionName("customized_setup_1")
.data(data)
.build();
InsertResp insertResp = client.insert(insertReq);
// 6.1、1:N 向量搜索
List<List<Float>> query_vectors = Arrays.asList(
Arrays.asList(0.3580376395471989f, -0.6023495712049978f, 0.18414012509913835f, -0.26286205330961354f, 0.9029438446296592f));
SearchReq searchReq = SearchReq.builder()
.collectionName("customized_setup_1")
.data(query_vectors)
.topK(3) // The number of results to return
.build();
SearchResp searchResp = client.search(searchReq);
// 6.2 M:N 向量搜索
query_vectors = Arrays.asList(
Arrays.asList(0.3580376395471989f, -0.6023495712049978f, 0.18414012509913835f, -0.26286205330961354f, 0.9029438446296592f),
Arrays.asList(0.19886812562848388f, 0.06023560599112088f, 0.6976963061752597f, 0.2614474506242501f, 0.838729485096104f)
);
searchReq = SearchReq.builder()
.collectionName("customized_setup_1")
.data(query_vectors)
.topK(2)
.build();
searchResp = client.search(searchReq);
// 6.3 带标量过滤的向量搜索
searchReq = SearchReq.builder()
.collectionName("customized_setup_1")
.data(query_vectors)
.outputFields(Arrays.asList("my_id", "my_vector"))
.filter("color_tag like \"red%\"") // like前缀匹配
.topK(5)
.build();
searchResp = client.search(searchReq);
// 7、从内存中释放collection
ReleaseCollectionReq releaseCollectionReq = ReleaseCollectionReq.builder()
.collectionName("customized_setup_2")
.build();
// 获取collection的加载状态
res = client.getLoadState(GetLoadStateReq.builder().collectionName("customized_setup_1").build());
System.out.println("customized_setup_1 已加载: " + res);
}
上一篇 2022年7月装机
下一篇 Arthas
页面信息
location:
protocol
: host
: hostname
: origin
: pathname
: href
: document:
referrer
: navigator:
platform
: userAgent
: