java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > SpringBoot 整合Redis Stack

SpringBoot 整合Redis Stack 构建本地向量数据库相似性查询的方法

作者:小码农叔叔

本文介绍了向量数据库的概念、特点和使用场景,并列举了常见的向量数据库解决方案,如Milvus、Faiss、Pinecone和Weaviate,同时,通过整合Spring Boot实现向量数据库的相似性搜索,展示了如何使用RedisStack作为向量数据库进行数据存储和检索,感兴趣的朋友一起看看吧

一、前言

AI工具的使用让数据检索的需求场景变得越来越多,也越来越复杂,传统的关系型数据库尽管可以满足日常各类关系数据的存储,但是遇到一些文档类型,非结构化的数据时,其作用和效果就会打折扣,而且数据不仅仅是存储的需要,也需要能够满足数据分析的场景,这种情况下,就可以考虑使用向量数据库了。

二、向量数据库介绍

2.1 什么是向量数据库

向量数据库是一种优化后的数据库系统,专门设计用于高效地存储和检索高维向量数据。这些向量数据通常是从原始数据(如文本、图像、音频等)中提取的特征表示。向量数据库的核心优势在于其能够快速地进行相似性搜索,即找到与给定向量最相似的向量。

2.2 向量数据库特点

向量数据库主要具备如下特征:

2.3 向量数据库使用场景

在下面的一些场景中可以考虑使用向量数据库

三、常用的向量数据库解决方案

向量数据库的选择也比较多,下面列举几种常用的向量数据库解决方案。

3.1 Milvus

3.1.1 Milvus是什么

Milvus 是一个高性能的向量数据库,由 Zilliz 开发并维护。它支持多种相似性度量(如欧氏距离、余弦相似度等),并使用高效的索引结构(如 IVF、HNSW 等)来加速相似性搜索。Milvus 可以处理大规模的向量数据集,并支持分布式部署,适用于多种应用场景。Milvus 在推荐系统、图像搜索、自然语言处理等领域表现出色,能够快速地进行相似性搜索,找到与给定向量最相似的向量。

git地址 : GitHub - milvus-io/milvus: A cloud-native vector database, storage for next generation AI applications

3.1.2 Milvus主要特点

Milvus具有如下特点:

3.2 Faiss

3.2.1 Faiss是什么

Faiss 是一个开源库,专注于大规模向量相似性搜索和聚类。它支持多种相似性度量(如欧氏距离、余弦相似度等),并使用高效的索引结构(如 IVF(Inverted File)、HNSW(Hierarchical Navigable Small World)等)来加速搜索过程。Faiss 可以在 CPU 和 GPU 上运行,适用于多种应用场景。

git地址 : GitHub - facebookresearch/faiss: A library for efficient similarity search and clustering of dense vectors.

3.2.2 Faiss主要特点

Faiss具有如下特点:

3.3 Pinecone

3.3.1 Pinecone是什么

Pinecone 是一个云原生的向量数据库,专为高效地存储和检索大规模向量数据而设计。它旨在简化向量数据的存储和检索过程,提供高性能的相似性搜索功能。Pinecone 提供了高性能的相似性搜索功能,支持实时和批量处理,适用于推荐系统、图像搜索、自然语言处理等多种应用场景。

官方地址:The vector database to build knowledgeable AI | Pinecone

3.3.2 Pinecone主要特点

Pinecone具备如下特点:

3.4 Weaviate

3.4.1 Weaviate介绍

Weaviate 是一个开源的向量搜索引擎,由 Semitechnologies 开发并维护。它不仅支持高效的向量数据存储和检索,还结合了图数据模型,使其在处理复杂数据关系方面表现出色。Weaviate 适用于推荐系统、图像搜索、自然语言处理等多种应用场景。Weaviate 支持多种相似性度量(如余弦相似度、欧氏距离等),并使用高效的索引结构来加速搜索过程。Weaviate 提供了丰富的 API 和 SDK,支持多种编程语言,方便开发者集成。

3.4.2 Weaviate主要特点

Weaviate具备如下特点

3.5 Redis Stack

3.5.1 Redis Stack介绍

Redis Stack 是 Redis 的一个扩展版本,它不仅包含了 Redis 的核心功能,还增加了多个模块,如 RediSearch(全文搜索)、RedisGraph(图数据库)、RedisTimeSeries(时间序列数据库)和 RedisAI(AI推理)。这些模块使得 Redis Stack 成为一个功能更加强大的数据库系统,适用于多种应用场景。

3.5.2 Redis Stack核心模块

3.5.3 Redis Stack主要特点

3.5.4 Redis Stack应用场景

Redis Stack主要有下面的应用场景

四、Redis向量数据库介绍

4.1 redis向量数据库是什么

Redis 是一个开源(BSD 许可)的内存数据结构存储,用作数据库、缓存、消息代理和流式处理引擎。Redis 提供数据结构,例如字符串、哈希、列表、集合、带范围查询的有序集合、位图、超对数日志、地理空间索引和流。

Redis 搜索和查询, 扩展了 Redis OSS 的核心功能,因此可以将 Redis 用作向量数据库

4.2 向量检索核心原理

向量检索(Vector Search)的核心原理是通过将文本或数据表示为高维向量,并在查询时根据向量的相似度进行搜索。具体来说,向量检索过程涉及以下核心几点。

4.2.1 匹配原理

技术关键点:

4.2.2 具体实现过程

在代码中操作时,主要包括下面几步:

五、整合springboot 实现向量数据库相似性搜索

接下来使用Redis Stack作为向量数据库,并整合springboot 实现向量数据库相似性搜索的过程。

5.1 前置准备

5.1.1 docker搭建Redis-Stack

使用下面的docker命令搭建Redis-Stack

docker run -d --name redis-stack -v /redis-data:/data -p 6379:6379 -p 8001:8001 redis/redis-stack:latest

也可以使用下面的docker compose命令启动容器

version: '3'
services:
  redis-stack:
    image: redis/redis-stack
    ports:
      - 6379:6379
  redis-insight:
    image: redislabs/redisinsight:latest
    ports:
      - 5540:5540

可以通过 IP:8001 访问 Redis-Stack 的web控制台,这里在后面将会用于存储程序中的文本嵌入数据

5.1.2 导入maven依赖

在你的maven工程中导入如下依赖

<properties>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.2.4</version>
    <relativePath/>
</parent>
<dependencies>
    <dependency>
        <groupId>com.alibaba.cloud.ai</groupId>
        <artifactId>spring-ai-alibaba-starter</artifactId>
        <version>1.0.0-M2.1</version>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>2.0.35</version>
    </dependency>
    <dependency>
        <groupId>com.squareup.okhttp3</groupId>
        <artifactId>okhttp</artifactId>
        <version>4.12.0</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <scope>provided</scope>
    </dependency>
    <!-- https://mvnrepository.com/artifact/com.alibaba/dashscope-sdk-java -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>dashscope-sdk-java</artifactId>
        <version>2.16.9</version>
    </dependency>
    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
        <version>4.5.13</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/io.milvus/milvus-sdk-java -->
    <dependency>
        <groupId>io.milvus</groupId>
        <artifactId>milvus-sdk-java</artifactId>
        <version>2.4.6</version>
    </dependency>
</dependencies>
<repositories>
    <repository>
        <id>spring-milestones</id>
        <name>Spring Milestones</name>
        <url>https://repo.spring.io/milestone</url>
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
    </repository>
</repositories>

5.1.3 获取apikey

下文中将会先演示使用云厂商提供的向量数据库,所以这里使用百炼平台的,需要先获取apikey

获取apikey,可以去阿里云的百炼平台获取,然后在模型广场中向量模型那里选择一种文本向量使用

5.1.4 添加配置文件

在工程的配置文件中增加下面的配置内容

server:
  port: 8081
spring:
  ai:
    dashscope:
      api-key: 你的apikey
      embedding:
        options:
          model: text-embedding-v2

5.2 文本嵌入代码实现过程

如何用一段话去数据库查找数据?答案是向量求相似度。相似度越高,返回的数据就越靠前,比如大家熟知的搜索引擎es,你输入一段文本去查询,es会根据算分将得分最高的文档放在前面返回给你,在大模型中,嵌入模型就可以帮我们做到这件事,所以第一件事情需要将文本中的句子向量化,然后和向量数据库中的向量进行比较,找到最相似的向量。下面使用代码演示下这个过程。

5.2.1 添加一个测试接口

添加一个测试接口,参考下面的代码

package com.congge.web;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.embedding.EmbeddingModel;
import org.springframework.ai.embedding.EmbeddingResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Map;
@RestController
@Slf4j
public class DocumentController {
    private final EmbeddingModel embeddingModel;
    @Autowired
    public DocumentController(EmbeddingModel embeddingModel) {
        this.embeddingModel = embeddingModel;
    }
    @GetMapping("/ai/embedding")
    public Map embed(@RequestParam(value = "message", defaultValue = "Tell me a joke") String message) {
        EmbeddingResponse embeddingResponse = this.embeddingModel.embedForResponse(List.of(message));
        return Map.of("embedding", embeddingResponse);
    }
    //localhost:8081/embedding
    @GetMapping("/embedding")
    public Object embeddingTest() {
        // 文本嵌入
        float[] embed = embeddingModel.embed("你好,我是小码农叔叔");
        log.info("文本转换得到的向量: {}", embed);
        return embed;
    }
}

两个接口均可用于测试,选择第二个测试,请求之后,返回了很多向量的参数

5.3 ETL Pipeline

ETL是提取、转换、加载的缩写,从原始的文档到数据库需要经历提取(.doc、.ppt、.xlsx等)、转换(数据结构化、清理数据、数据分块)、写入向量数据库。这个过程可以进行多种处理,确保最后的数据适合AI问答。

SpringAI提供了ETL框架。它是搭建知识库框架的基石。

5.3.1 Spring AI ETL 介绍

在AI应用程序中,数据处理是至关重要的一部分。Spring AI提供了一个强大的ETL框架,帮助开发者高效地进行数据提取、转换和加载操作。通过这个框架,你可以轻松地将数据准备好用于AI模型的训练和推理。

5.3.2 Spring AI 处理ETL完整过程

与传统的etl过程稍有不同,springai 中etl 的完整过程如下:

5.3.3 Spring AI ETL 技术框架

5.3.3.1 读取器 DocumentReader

DocumentReader ,文档读取器:

5.3.3.2 文档转换器 DocumentTransformer

文档转换器,处理文档:

5.3.3.3 文档写入器 DocumentWriter

文档写入器:

使用这几个组件,完整的执行流程如下:

5.4 Spring AI 文档读取操作演示

5.4.1 文件读取代码演示

TikaDocumentReader 比较全能,大部分文件都可读取,支持的文件格式可以参考官方文档。如果是比较个性化文档的场景,最好自己实现一个Reader,便于业务扩展。pom文件中导入下面的依赖。

        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-tika-document-reader</artifactId>
        </dependency>

从输入流读取文件,参考下面的接口代码

@SneakyThrows
@PostMapping("read/multipart-file")
public void readMultipartFile(@RequestParam MultipartFile file) {
    // 从IO流中读取文件
    Resource resource = new InputStreamResource(file.getInputStream());
    List<Document> documents = new TikaDocumentReader(resource)
            .read();
}

上传一个本地的文档,接口测试效果,可以看到被分割成很多片段了

从本地文件读取,参考下面的接口代码

/**
 * 从本地文件读取
 * @return
 */
@GetMapping("/read/from-local-file")
public Object readLocalFile() {
    FileSystemResource resource = new FileSystemResource("E:\\工作空间\\wzlocal\\Java编码规范V1.0.pdf");
    List<Document> documents = new TikaDocumentReader(resource)
            .read();
    return documents;
}

调用一下接口,使用相同的文件,得到类似的效果

5.5 Spring AI 文档转换操作演示

在上一步的操作中,读取返回的是Document对象,而是DocumentETL Pipeline的核心对象,它包含了文档的元数据和内容。接下来就需要对文档进行转换。此时就要用到内容转换器。包括内容转换器和元数据转换器。

内容转换器

元数据转换器

5.5.1 添加测试接口

参考下面的代码

/**
 * 分割后的文档列表  localhost:8081/doc/split
 * @param file
 * @return
 * @throws Exception
 */
@PostMapping("/doc/split")
public Object docSplit(@RequestParam MultipartFile file) throws Exception{
    InputStreamResource inputStreamResource = new InputStreamResource(file.getInputStream());
    List<Document> documentList = new TikaDocumentReader(inputStreamResource).read();
    // 分割后的文档列表
    List<Document> splitDocList = new TokenTextSplitter().split(documentList);
    List<String> contentList = splitDocList.stream().map(Document::getContent).collect(Collectors.toList());
    return contentList;
}

测试一下接口,可以看到被转换后的文档分割成多个片段

5.6 文档存储

经过前面的步骤,我们得到了一个文档列表,为了方便后续的文档搜索,需要将文档进行存储,文档存储到哪里去呢?即向量数据库。上面准备阶段搭建的Redis-Stack即是用于做向量数据库使用的。下面看代码中的操作过程。

5.6.1 导入依赖文件

导入下面的依赖坐标

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-tika-document-reader</artifactId>
    <version>1.0.0-M3</version>
</dependency>
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-redis-store</artifactId>
    <version>1.0.0-M3</version>
</dependency>
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
</dependency>

5.6.2 配置redis信息

在配置文件中添加redis的信息

spring:
  ai:
    dashscope:
      api-key: 你的apikey
      embedding:
        options:
          model: text-embedding-v2
    vectorstore:
      redis:
        uri: redis://IP:6379
  data:
    redis:
      database: 0
      timeout: 10s
      lettuce:
        pool:
          # 连接池最大连接数
          max-active: 200
          # 连接池最大阻塞等待时间(使用负值表示没有限制)
          max-wait: -1ms
          # 连接池中的最大空闲连接
          max-idle: 10
          # 连接池中的最小空闲连接
          min-idle: 0
      repositories:
        enabled: false
      host: redis地址

5.6.3 增加一个redis的向量数据库配置类

如果在你的项目里面有用到redis,需要先禁用RedisVectorStoreAutoConfiguration。这是SpringAI自动配置RedisStack的向量数据库连接,会导致Redis的连接配置冲突。参考下面的代码。

package com.congge.config;
import lombok.AllArgsConstructor;
import org.springframework.ai.autoconfigure.vectorstore.redis.RedisVectorStoreAutoConfiguration;
import org.springframework.ai.autoconfigure.vectorstore.redis.RedisVectorStoreProperties;
import org.springframework.ai.embedding.EmbeddingModel;
import org.springframework.ai.vectorstore.RedisVectorStore;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.data.redis.RedisConnectionDetails;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.JedisPooled;
@Configuration
// 禁用SpringAI提供的RedisStack向量数据库的自动配置,会和Redis的配置冲突。
@EnableAutoConfiguration(exclude = {RedisVectorStoreAutoConfiguration.class})
// 读取RedisStack的配置信息
@EnableConfigurationProperties({RedisVectorStoreProperties.class})
@AllArgsConstructor
public class RedisVectorConfig {
    /**
     * 创建RedisStack向量数据库
     *
     * @param embeddingModel 嵌入模型
     * @param properties     redis-stack的配置信息
     * @return vectorStore 向量数据库
     */
    @Bean
    public VectorStore vectorStore(EmbeddingModel embeddingModel,
                                   RedisVectorStoreProperties properties,
                                   RedisConnectionDetails redisConnectionDetails) {
        RedisVectorStore.RedisVectorStoreConfig config = RedisVectorStore.RedisVectorStoreConfig.builder().withIndexName(properties.getIndex()).withPrefix(properties.getPrefix()).build();
        return new RedisVectorStore(config, embeddingModel,
                new JedisPooled(redisConnectionDetails.getStandalone().getHost(),
                        redisConnectionDetails.getStandalone().getPort()
                        , redisConnectionDetails.getUsername(),
                        redisConnectionDetails.getPassword()),
                properties.isInitializeSchema());
    }
}

5.6.4 增加嵌入文档接口

在上面的VectorStore配置中我们提供了EmbeddingModel,调用vectorStore.add(splitDocuments)底层会把文档给EmbeddingModel把文本变成向量然后再存入向量数据库。参考下面的代码。

/**
 * 文档保存到向量数据库   localhost:8081/doc/embedding
 * @param file
 * @return
 * @throws Exception
 */
@PostMapping("/doc/embedding")
public Object docEmbedding(@RequestParam MultipartFile file) throws Exception{
    TikaDocumentReader tikaDocumentReader = new TikaDocumentReader(new InputStreamResource(file.getInputStream()));
    List<Document> splitDocuments = new TokenTextSplitter().apply(tikaDocumentReader.read());
    // 存入向量数据库,这个过程会自动调用embeddingModel,将文本变成向量存入
    vectorStore.add(splitDocuments);
    return true;
}

调用一下接口

接口执行成功后,在Redis-Stack的控制台界面可以看到数据存入了redis中

5.6.5 增加文档检索接口

数据存入进去之后接下来就可以进行搜索了,添加下面文档检索的接口

/**
 * 关键字检索文档  localhost:8081/doc/query?keyword=面向对象
 * @param keyword
 * @return
 */
@GetMapping("/doc/query")
public List<Document> query(@RequestParam String keyword) {
    List<Document> documentList = vectorStore.similaritySearch(keyword);
    return documentList;
}

六、写在文末

本文通过较大的篇幅详细介绍了常用的向量数据库解决方案,并通过搭建redis stack环境,结合代码演示了如何基于redis stack作为向量数据库进行使用,希望对看到的同学有用,本篇到此介绍,感谢观看。

到此这篇关于SpringBoot 整合Redis Stack 构建本地向量数据库相似性查询的文章就介绍到这了,更多相关SpringBoot 整合Redis Stack 内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文