Bootstrap

Spring Boot整合Redis Stack构建本地向量数据库相似性查询

Spring Boot整合Redis Stack构建本地向量数据库相似性查询

在微服务架构中,数据的高效存储与快速查询是至关重要的。Redis作为一个高性能的内存数据结构存储系统,不仅可以用作缓存、消息代理,还可以扩展为向量数据库,实现高效的相似性搜索。本文将详细介绍如何使用Spring Boot整合Redis Stack,构建本地向量数据库,并进行相似性查询。

一、Redis Stack简介

Redis Stack(也称为Redis Enterprise或Redis Cloud)是一个全面的Redis解决方案,它包括了Redis数据库、Redis搜索、Redis AI、Redis JSON等多个模块,为开发者提供了丰富的数据结构和强大的功能。其中,Redis搜索模块允许我们将Redis用作向量数据库,进行相似性搜索。

二、环境准备

在开始之前,我们需要准备以下环境:

  1. JDK:确保已安装Java Development Kit(JDK),版本建议为17或更高。
  2. Maven:用于构建和管理Java项目依赖。
  3. Docker:用于运行Redis Stack容器。
  4. Spring Boot:用于构建微服务应用。
三、Redis Stack安装与配置

我们将使用Docker来安装Redis Stack。以下是安装步骤:

  1. 拉取Redis Stack镜像

    docker pull redis/redis-stack
    
  2. 运行Redis Stack容器

    docker run -d --name redis-stack -p 6379:6379 -p 5540:5540 redis/redis-stack
    

    这将启动Redis Stack容器,并暴露Redis服务(端口6379)和Redis Insight管理界面(端口5540)。

  3. 访问Redis Insight

    打开浏览器,访问http://localhost:5540,即可进入Redis Insight管理界面,进行Redis数据库的可视化管理。

四、Spring Boot项目构建

接下来,我们将使用Spring Boot构建一个微服务应用,用于与Redis Stack进行交互,实现向量数据库的相似性查询。

  1. 创建Spring Boot项目

    使用Spring Initializr或Maven命令创建一个新的Spring Boot项目,并添加以下依赖:

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>
        <!-- 其他依赖,如Spring AI(可选) -->
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-redis-spring-boot-starter</artifactId>
            <version>${spring-ai.version}</version>
        </dependency>
    </dependencies>
    
  2. 配置Redis连接

    application.ymlapplication.properties文件中配置Redis连接信息:

    spring:
      redis:
        host: localhost
        port: 6379
        # 其他配置,如密码、连接池等(可选)
    
  3. 创建Redis配置类

    创建一个Redis配置类,用于配置RedisTemplate和其他必要的Bean:

    @Configuration
    public class RedisConfig {
    
        @Bean
        public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
            RedisTemplate<String, Object> template = new RedisTemplate<>();
            template.setConnectionFactory(redisConnectionFactory);
            template.setKeySerializer(new StringRedisSerializer());
            template.setValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class));
            return template;
        }
    
        // 其他配置,如HashOperations、ValueOperations等(可选)
    }
    
  4. 实现向量存储与查询

    为了将文本或数据转换为向量,并使用Redis进行存储和查询,我们需要一个嵌入模型(如Word2Vec、BERT等)来生成向量。这里,我们假设已经有一个向量化工具(如vectorStore),它可以将指定的字段转换为高维向量,并存储在Redis中。

    @Service
    public class VectorService {
    
        @Autowired
        private RedisTemplate<String, Object> redisTemplate;
    
        // 存储向量到Redis
        public void storeVector(String key, double[] vector) {
            // 将向量序列化为字符串或其他可存储的格式(如JSON)
            String vectorStr = Arrays.toString(vector);
            redisTemplate.opsForValue().set(key, vectorStr);
        }
    
        // 查询与给定向量最相似的K个向量
        public List<String> similaritySearch(double[] queryVector, int k) {
            // 将查询向量转换为与存储在Redis中相同格式的字符串(可选)
            String queryVectorStr = Arrays.toString(queryVector);
    
            // 使用Redis的搜索功能进行相似性搜索(这里需要实现具体的搜索逻辑)
            // 假设有一个`vectorStore.similaritySearch`方法可以进行相似性计算
            // List<Document> results = vectorStore.similaritySearch(queryVector, k);
            // 返回结果(这里仅为示例,实际应返回相似向量的键或其他相关信息)
            // return results.stream().map(Document::getKey).collect(Collectors.toList());
    
            // 由于Redis原生不支持向量搜索,这里可以使用第三方库或自定义实现
            // 以下是一个简单的示例,使用余弦相似度进行手动计算(不推荐用于生产环境)
            Map<String, Double> similarityMap = new HashMap<>();
            redisTemplate.keys("*").forEach(redisKey -> {
                String vectorStr = (String) redisTemplate.opsForValue().get(redisKey);
                double[] storedVector = Arrays.stream(vectorStr.replaceAll("[\\[\\]\\s]", "").split(","))
                        .mapToDouble(Double::parseDouble).toArray();
                double similarity = cosineSimilarity(queryVector, storedVector);
                similarityMap.put(redisKey, similarity);
            });
    
            // 按相似度排序并返回前K个结果
            List<Map.Entry<String, Double>> sortedEntries = similarityMap.entrySet().stream()
                    .sorted((e1, e2) -> Double.compare(e2.getValue(), e1.getValue()))
                    .collect(Collectors.toList());
    
            return sortedEntries.stream().limit(k).map(Map.Entry::getKey).collect(Collectors.toList());
        }
    
        // 计算余弦相似度
        private double cosineSimilarity(double[] a, double[] b) {
            double dotProduct = 0.0;
            double normA = 0.0;
            double normB = 0.0;
            for (int i = 0; i < a.length; i++) {
                dotProduct += a[i] * b[i];
                normA += a[i] * a[i];
                normB += b[i] * b[i];
            }
            return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
        }
    }
    

    注意:上述代码中的vectorStore.similaritySearch方法是一个假设的方法,Redis原生并不支持向量搜索。在实际应用中,我们可以使用Redis的搜索模块(需要安装Redis Search插件)或第三方库(如Elasticsearch、FAISS等)来实现高效的向量搜索。此外,上述代码中的余弦相似度计算是手动实现的,仅用于示例,不推荐在生产环境中使用。

  5. 创建控制器

    创建一个控制器,用于接收客户端的请求,并调用VectorService进行向量存储和查询:

    @RestController
    @RequestMapping("/vectors")
    public class VectorController {
    
        @Autowired
        private VectorService vectorService;
    
        // 存储向量
        @PostMapping("/store")
        public ResponseEntity<Void> storeVector(@RequestBody Map<String, double[]> requestBody) {
            String key = requestBody.keySet().iterator().next();
            double[] vector = requestBody.get(key);
            vectorService.storeVector(key, vector);
            return ResponseEntity.ok().build();
        }
    
        // 查询相似向量
        @GetMapping("/search")
        public ResponseEntity<List<String>> similaritySearch(@RequestParam double[] queryVector, @RequestParam int k) {
            List<String> similarKeys = vectorService.similaritySearch(queryVector, k);
            return ResponseEntity.ok(similarKeys);
        }
    }
    
  6. 运行应用

    使用Maven或IDE运行Spring Boot应用,然后可以通过Postman或其他HTTP客户端发送请求,进行向量存储和查询。

五、总结

通过上述步骤,我们成功使用Spring Boot整合了Redis Stack,并构建了一个本地向量数据库,实现了相似性查询。然而,需要注意的是,Redis原生并不支持向量搜索,因此在实际应用中,我们需要借助Redis的搜索模块、

;