Skip to content

Milvus ​

Milvus 是可选的替代存储方案。当前 Java MossKB 的已运行检索后端是 PostgreSQL + pgvector,未将 Milvus 列为已经接通的运行路径。

1. 什么是向量数据库? ​

向量数据库是一种专门用于存储和查询高维向量的数据库,广泛应用于文本、语音、图像、视频等数据的存储和检索。相较于传统数据库,向量数据库不仅支持 CRUD(创建、读取、更新、删除)操作,还能高效地执行相似性搜索。

2. Milvus 介绍 ​

Milvus 是一款云原生的向量数据库,具备高可用性、高性能、易扩展等特性,主要用于海量向量数据的实时检索。它基于 FAISS、Annoy、HNSW 等向量搜索库构建,核心目标是优化稠密向量相似性检索。

Milvus 具有以下特性:

  • 数据存储与查询优化:支持数据分区分片、数据持久化、增量数据摄取、标量与向量混合查询。
  • 高性能检索:提供时间旅行(Time Travel)、高效索引结构等优化方案。
  • 云原生架构:采用存储计算分离架构,计算节点支持横向扩展,保障高吞吐量。

3. 在 Docker 中安装 Milvus ​

Milvus 提供官方的 Docker 镜像,建议使用 Docker 进行安装,以保证稳定性和易维护性。

3.1 安装 Docker 和 Docker Compose ​

如果尚未安装 Docker,可以使用以下命令进行安装(适用于 Linux):

sh
# 更新软件包
sudo apt update
sudo apt install -y docker.io docker-compose

3.2 启动 Milvus ​

使用官方提供的 docker-compose.yml 文件启动 Milvus。

sh
mkdir /opt/docker-compose-yml/milvus -p && cd /opt/docker-compose-yml/milvus
wget https://github.com/milvus-io/milvus/releases/download/v2.5.4/milvus-standalone-docker-compose.yml -O docker-compose.yml

以下为历史部署示例,实际部署应使用所选发行包配套的 Compose 配置

yml
version: "3.5"

services:
  etcd:
    container_name: milvus-etcd
    image: quay.io/coreos/etcd:v3.5.16
    environment:
      - ETCD_AUTO_COMPACTION_MODE=revision
      - ETCD_AUTO_COMPACTION_RETENTION=1000
      - ETCD_QUOTA_BACKEND_BYTES=4294967296
      - ETCD_SNAPSHOT_COUNT=50000
    volumes:
      - ${DOCKER_VOLUME_DIRECTORY:-.}/volumes/etcd:/etcd
    command: etcd -advertise-client-urls=http://127.0.0.1:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd
    healthcheck:
      test: ["CMD", "etcdctl", "endpoint", "health"]
      interval: 30s
      timeout: 20s
      retries: 3

  minio:
    container_name: milvus-minio
    image: minio/minio:RELEASE.2023-03-20T20-16-18Z
    environment:
      MINIO_ACCESS_KEY: minioadmin
      MINIO_SECRET_KEY: minioadmin
    ports:
      - "9001:9001"
      - "9000:9000"
    volumes:
      - ${DOCKER_VOLUME_DIRECTORY:-.}/volumes/minio:/minio_data
    command: minio server /minio_data --console-address ":9001"
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
      interval: 30s
      timeout: 20s
      retries: 3

  standalone:
    container_name: milvus-standalone
    image: milvusdb/milvus:v2.5.4
    command: ["milvus", "run", "standalone"]
    security_opt:
      - seccomp:unconfined
    environment:
      ETCD_ENDPOINTS: etcd:2379
      MINIO_ADDRESS: minio:9000
    volumes:
      - ${DOCKER_VOLUME_DIRECTORY:-.}/volumes/milvus:/var/lib/milvus
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:9091/healthz"]
      interval: 30s
      start_period: 90s
      timeout: 20s
      retries: 3
    ports:
      - "19530:19530"
      - "9091:9091"
    depends_on:
      - "etcd"
      - "minio"

networks:
  default:
    name: milvus

启动 Milvus

shell
docker-compose up -d

or

shell
docker compose up -d

如果是单机部署,这 3 个容器都要启动码? 是的。在这个单机部署的方案中,三个容器都是必需的:

  • etcd:负责存储 Milvus 的元数据。
  • minio:用于存储实际的数据文件。
  • milvus-standalone:运行 Milvus 的核心服务。

除非有其他方式来提供 etcd 和 minio 服务,否则这三个容器需要一起启动。

此时 Milvus 运行在 localhost:19530,可使用 docker ps 查看容器状态。

4. Java 集成 Milvus ​

4.1 必要的 Maven 依赖 ​

Milvus 官方提供了 milvus-sdk-java,其版本需与 Milvus 服务器版本匹配。此外,需要 grpc-netty-shaded 作为 gRPC 的底层通信库。jersey-server 依赖通常用于 RESTful API,如果不使用 REST 方式,可省略。

必要依赖:

依赖示例省略版本声明,由项目的统一依赖管理配置提供。

xml
<dependencies>
    <dependency>
        <groupId>io.grpc</groupId>
        <artifactId>grpc-netty-shaded</artifactId>

    </dependency>
    <dependency>
        <groupId>io.milvus</groupId>
        <artifactId>milvus-sdk-java</artifactId>

        <exclusions>
            <exclusion>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-log4j12</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
</dependencies>

4.2 连接 Milvus ​

java
private MilvusClientV2 clientV2;
try {
    ConnectConfig connectConfig = ConnectConfig.builder()
            .uri("localhost:19530")
            .token("your-token")
            .build();
    this.clientV2 = new MilvusClientV2(connectConfig);
} catch (Exception e) {
    e.printStackTrace();
}

4.3 创建集合 ​

java
public void createCollection(String collectionName) {
    boolean collectionExists = true;
    try {
        DescribeCollectionReq describeCollectionReq = DescribeCollectionReq.builder()
                .collectionName(collectionName)
                .build();
        clientV2.describeCollection(describeCollectionReq);
    } catch (Exception e) {
        collectionExists = false;
    }

    if (!collectionExists) {
        CreateCollectionReq createReq = CreateCollectionReq.builder()
                .collectionName(collectionName)
                .dimension(1536) // 向量维度
                .idType(DataType.VarChar) // ID 类型
                .metricType("IP") // 内积相似度
                .build();
        clientV2.createCollection(createReq);

        GetLoadStateReq loadStateReq = GetLoadStateReq.builder()
                .collectionName(collectionName)
                .build();
        boolean loadState = clientV2.getLoadState(loadStateReq);
    }
}

4.4 插入或更新数据 ​

java
public void upsert(String collectionName, List<String> documents, List<Map<String, String>> metadata, List<String> ids, List<List<Float>> embeddings) {
    createCollection(collectionName);
    List<JSONObject> data = new ArrayList<>();
    for (int i = 0; i < documents.size(); i++) {
        Map<String, Object> record = new HashMap<>();
        record.put("id", ids.get(i));
        record.put("vector", embeddings.get(i));
        record.put("metadata", metadata.get(i));
        data.add(new JSONObject(record));
    }

    UpsertReq upsertReq = UpsertReq.builder()
            .collectionName(collectionName)
            .data(data)
            .build();
    clientV2.upsert(upsertReq);
}

4.5 进行向量搜索 ​

java
List<List<Float>> embeddings = embeddingFunction.createEmbedding();
Map<String, Object> params = new HashMap<>();
params.put("radius", 0.5); // 相似度阈值

SearchReq searchReq = SearchReq.builder()
        .collectionName(collectionName)
        .data(embeddings)
        .searchParams(params)
        .outputFields(Arrays.asList("id", "metadata", "vector"))
        .build();
SearchResp searchResp = clientV2.search(searchReq);

4.6 删除数据和集合 ​

删除对象

java
public void delete(List<String> ids, String collectionName) {
    DeleteReq deleteReq = DeleteReq.builder()
            .collectionName(collectionName)
            .ids(new ArrayList<>(ids))
            .build();
    clientV2.delete(deleteReq);
}

删除集合

java
public void deleteCollection(String collectionName) {
    DropCollectionReq dropReq = DropCollectionReq.builder()
            .collectionName(collectionName)
            .build();
    clientV2.dropCollection(dropReq);
}

5. 结论 ​

Milvus 作为领先的向量数据库,能够高效地存储和检索高维数据。通过 Java SDK,可以轻松实现连接、数据管理及搜索功能。对于 Java 开发者而言,grpc-netty-shaded 是必须的,而 jersey-server 仅在使用 REST API 时才需要。

如需深入了解,请参考 Milvus 官方文档。

MIT Licensed