SpringBoot + MongoDB全栈实战:从架构原理到AI集成

SpringBoot + MongoDB全栈实战:从架构原理到AI集成

本文深入探讨MongoDB的架构设计,并结合SpringBoot提供完整开发实例,涵盖高可用部署、向量搜索和AI集成,助力开发者构建现代化应用。

一、MongoDB架构设计原理深度剖析

1.1 核心架构设计:分布式文档数据库的基石

MongoDB采用分布式、面向文档的架构设计,其核心思想是将传统关系型数据库的表、行、列概念转换为更灵活的集合、文档、字段模型。

三大核心组件:

  1. 分片集群(Sharding) - 水平扩展的基石

    • 数据自动分区到多个分片
    • 支持海量数据存储和吞吐量
    • 分片键决定数据分布策略
  2. 副本集(Replica Set) - 高可用的保障

    • 一主多从的复制架构
    • 自动故障检测和转移
    • 数据冗余和读写分离
  3. 查询路由(Mongos) - 智能流量管理

    • 统一的集群访问入口
    • 查询分析和路由优化
    • 隐藏后端分片复杂性

1.2 存储引擎演进:性能与可靠性的平衡

WiredTiger存储引擎(默认)的核心特性:

  • 文档级并发控制:不同文档可以并行读写,大幅提升并发性能
  • 快照隔离:读取操作看到的是某个时间点的数据一致性视图
  • 压缩存储:支持snappy、zlib等多种压缩算法,节省存储空间
  • 检查点机制:定期将内存数据持久化,保证崩溃恢复能力

与传统MMAPv1引擎对比:

  • 内存使用更高效
  • 磁盘空间利用率更高
  • 并发性能提升5-10倍

1.3 数据模型设计:灵活性与性能的完美结合

BSON格式的优势:

  • 类JSON的二进制表示,解析速度更快
  • 支持Date、Binary等丰富数据类型
  • 文档最大16MB,适合大多数业务场景

文档设计模式:

复制代码
嵌入式文档模式:适合一对少关系,数据局部性好
引用式文档模式:适合一对多关系,避免文档过大
混合式设计:根据查询模式灵活选择

1.4 副本集工作原理:自动故障转移的魔法

副本集成员角色:

  • Primary:主节点,处理所有写操作
  • Secondary:从节点,异步复制数据,支持读操作
  • Arbiter:仲裁节点,参与选举但不存储数据

选举机制:

  1. 节点间心跳检测,发现主节点不可用
  2. 符合条件的从节点发起选举
  3. 获得大多数投票的节点成为新主节点
  4. 客户端自动重连到新主节点

数据复制流程:

  1. 写操作进入主节点的oplog(操作日志)
  2. 从节点定期从主节点拉取oplog
  3. 从节点按顺序重放oplog中的操作
  4. 保证最终一致性

1.5 分片集群架构:无限扩展的艺术

分片集群核心组件:

  1. 配置服务器(Config Servers)

    • 存储集群元数据:分片信息、数据分布
    • 通常3个节点构成副本集
    • 轻量级服务,资源需求小
  2. 分片节点(Shard Nodes)

    • 实际数据存储位置
    • 每个分片可以是单机或副本集
    • 数据按分片键自动分布
  3. 查询路由(Mongos)

    • 无状态服务,可水平扩展
    • 解析查询,确定涉及的分片
    • 合并分片返回的结果

分片策略选择:

  • 范围分片:适合范围查询,但可能数据分布不均
  • 哈希分片:数据均匀分布,但范围查询效率低
  • 区域分片:基于标签的智能数据分布

二、SpringBoot整合MongoDB开发实战

2.1 项目配置与依赖

pom.xml配置:

xml 复制代码
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-mongodb</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

application.yml配置:

yaml 复制代码
spring:
  data:
    mongodb:
      uri: mongodb://localhost:27017/paoxiang
      auto-index-creation: true  # 自动创建索引
      
logging:
  level:
    org.springframework.data.mongodb.core: DEBUG  # 开启MongoDB操作日志

2.2 实体类设计:文档模型的Java映射

java 复制代码
/**
 * 文章实体类 - 展示MongoDB文档设计的最佳实践
 * 使用@Document注解映射集合
 * 使用@Id标识主键字段
 * 使用@Field自定义字段名
 */
@Document(collection = "articles")
@CompoundIndex(name = "title_content_idx", def = "{'title': 'text', 'content': 'text'}")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Article {
    
    @Id
    private String id;
    
    @Indexed(unique = true)  // 唯一索引
    @Field("title")
    private String title;
    
    @Field("content")
    private String content;
    
    @Field("author")
    private Author author;  // 内嵌文档
    
    @Field("tags")
    private List<String> tags;
    
    @Field("view_count")
    private Integer viewCount = 0;
    
    @Field("publish_date")
    private Date publishDate;
    
    @Field("embedding")
    private List<Double> embedding; // 向量嵌入字段,用于AI搜索
    
    @Field("metadata")
    private ArticleMetadata metadata;
    
    // 便捷构造函数
    public Article(String title, String content, Author author) {
        this.title = title;
        this.content = content;
        this.author = author;
        this.publishDate = new Date();
        this.tags = new ArrayList<>();
        this.viewCount = 0;
    }
}

/**
 * 作者信息 - 内嵌文档示例
 * 适合一对一或一对少关系
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Author {
    private String name;
    private String email;
    private String bio;
}

/**
 * 文章元数据 - 内嵌文档
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ArticleMetadata {
    private Integer wordCount;
    private Integer readTime; // 阅读时长(分钟)
    private String category;
    private Boolean featured = false;
    private String language = "zh-CN";
}

2.3 Repository数据访问层:声明式查询的威力

java 复制代码
/**
 * 文章数据访问接口
 * 继承MongoRepository获得基础CRUD能力
 * 方法名自动推导查询条件
 */
@Repository
public interface ArticleRepository extends MongoRepository<Article, String> {
    
    /**
     * 根据标题精确查找 - Spring Data自动实现
     */
    List<Article> findByTitle(String title);
    
    /**
     * 根据作者名称查找 - 嵌套属性查询
     */
    List<Article> findByAuthorName(String authorName);
    
    /**
     * 根据标签包含查询 - 数组包含条件
     */
    List<Article> findByTagsContaining(String tag);
    
    /**
     * 复杂查询:发布日期范围 + 标签包含
     * 使用@Query注解自定义MongoDB查询
     */
    @Query("{ 'publishDate': { $gte: ?0, $lte: ?1 }, 'tags': { $in: ?2 } }")
    List<Article> findArticlesByDateRangeAndTags(Date startDate, Date endDate, List<String> tags);
    
    /**
     * 文本搜索查询 - 全文索引搜索
     */
    @Query("{ $text: { $search: ?0 } }")
    List<Article> fullTextSearch(String keyword);
    
    /**
     * 分页查询 - 结合分页参数
     */
    Page<Article> findByTagsContaining(String tag, Pageable pageable);
    
    /**
     * 统计作者文章数量
     */
    @Query(value = "{}", fields = "{ 'author.name' : 1 }")
    List<Article> findAllAuthorNames();
}

2.4 Service业务逻辑层:事务与业务封装

java 复制代码
/**
 * 文章服务类 - 核心业务逻辑实现
 * 封装复杂业务操作,保证事务一致性
 */
@Service
@Slf4j
public class ArticleService {
    
    @Autowired
    private ArticleRepository articleRepository;
    
    @Autowired
    private MongoTemplate mongoTemplate;
    
    /**
     * 创建文章 - 基础创建操作
     */
    @Transactional
    public Article createArticle(Article article) {
        log.info("创建文章: {}", article.getTitle());
        
        // 数据验证和默认值设置
        if (article.getId() != null) {
            throw new IllegalArgumentException("新文章ID必须为空");
        }
        
        // 自动生成ID和创建时间
        article.setId(new ObjectId().toString());
        if (article.getPublishDate() == null) {
            article.setPublishDate(new Date());
        }
        if (article.getViewCount() == null) {
            article.setViewCount(0);
        }
        
        return articleRepository.save(article);
    }
    
    /**
     * 批量插入文章 - 提升性能
     */
    public List<Article> batchCreateArticles(List<Article> articles) {
        log.info("批量插入 {} 篇文章", articles.size());
        
        // 设置默认值
        articles.forEach(article -> {
            if (article.getId() == null) {
                article.setId(new ObjectId().toString());
            }
            if (article.getPublishDate() == null) {
                article.setPublishDate(new Date());
            }
        });
        
        return articleRepository.saveAll(articles);
    }
    
    /**
     * 根据ID查询文章 - 基础查询
     */
    public Article findById(String id) {
        return articleRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("文章不存在: " + id));
    }
    
    /**
     * 原子操作:递增浏览次数
     * 使用MongoTemplate实现原子更新,避免并发问题
     */
    public void incrementViewCount(String id) {
        Query query = new Query(Criteria.where("id").is(id));
        Update update = new Update().inc("viewCount", 1);
        mongoTemplate.updateFirst(query, update, Article.class);
        log.debug("文章 {} 浏览次数+1", id);
    }
    
    /**
     * 复杂聚合查询:按作者统计文章数和总浏览量
     * 展示MongoDB强大的聚合框架能力
     */
    public List<AuthorStats> getAuthorStatistics() {
        Aggregation aggregation = Aggregation.newAggregation(
            // 按作者分组
            Aggregation.group("author.name")
                .count().as("articleCount")           // 统计文章数
                .sum("viewCount").as("totalViews")    // 计算总浏览量
                .avg("viewCount").as("avgViews"),     // 计算平均浏览量
            // 按总浏览量降序排序
            Aggregation.sort(Sort.Direction.DESC, "totalViews")
        );
        
        AggregationResults<AuthorStats> results = 
            mongoTemplate.aggregate(aggregation, "articles", AuthorStats.class);
        
        return results.getMappedResults();
    }
    
    /**
     * 文本搜索与分页结合
     */
    public Page<Article> searchArticles(String keyword, Pageable pageable) {
        // 构建查询条件:标题或内容包含关键词
        Criteria criteria = new Criteria().orOperator(
            Criteria.where("title").regex(keyword, "i"),
            Criteria.where("content").regex(keyword, "i")
        );
        
        Query query = new Query(criteria).with(pageable);
        
        long total = mongoTemplate.count(query, Article.class);
        List<Article> articles = mongoTemplate.find(query, Article.class);
        
        return new PageImpl<>(articles, pageable, total);
    }
    
    /**
     * 作者统计DTO - 聚合结果映射
     */
    @Data
    @AllArgsConstructor
    public static class AuthorStats {
        private String authorName;
        private Long articleCount;
        private Long totalViews;
        private Double avgViews;
    }
}

2.5 Controller控制层:RESTful API设计

java 复制代码
/**
 * 文章REST API控制器
 * 提供完整的CRUD接口和业务接口
 */
@RestController
@RequestMapping("/api/articles")
@Slf4j
public class ArticleController {
    
    @Autowired
    private ArticleService articleService;
    
    /**
     * 创建文章 - POST /api/articles
     */
    @PostMapping
    public ResponseEntity<Article> createArticle(@RequestBody @Valid Article article) {
        try {
            Article saved = articleService.createArticle(article);
            log.info("成功创建文章: {}", saved.getTitle());
            return ResponseEntity.ok(saved);
        } catch (Exception e) {
            log.error("创建文章失败", e);
            return ResponseEntity.badRequest().build();
        }
    }
    
    /**
     * 获取文章详情 - GET /api/articles/{id}
     */
    @GetMapping("/{id}")
    public ResponseEntity<Article> getArticle(@PathVariable String id) {
        try {
            Article article = articleService.findById(id);
            // 增加浏览次数(异步处理更佳)
            articleService.incrementViewCount(id);
            return ResponseEntity.ok(article);
        } catch (RuntimeException e) {
            log.warn("查询文章不存在: {}", id);
            return ResponseEntity.notFound().build();
        }
    }
    
    /**
     * 搜索文章 - GET /api/articles/search
     */
    @GetMapping("/search")
    public ResponseEntity<Page<Article>> searchArticles(
            @RequestParam String keyword,
            @RequestParam(defaultValue = "0") int page,
            @RequestParam(defaultValue = "10") int size) {
        
        try {
            Pageable pageable = PageRequest.of(page, size, Sort.by("publishDate").descending());
            Page<Article> articles = articleService.searchArticles(keyword, pageable);
            return ResponseEntity.ok(articles);
        } catch (Exception e) {
            log.error("搜索文章失败: {}", keyword, e);
            return ResponseEntity.internalServerError().build();
        }
    }
    
    /**
     * 获取作者统计 - GET /api/articles/stats/authors
     */
    @GetMapping("/stats/authors")
    public ResponseEntity<List<ArticleService.AuthorStats>> getAuthorStats() {
        try {
            List<ArticleService.AuthorStats> stats = articleService.getAuthorStatistics();
            return ResponseEntity.ok(stats);
        } catch (Exception e) {
            log.error("获取作者统计失败", e);
            return ResponseEntity.internalServerError().build();
        }
    }
    
    /**
     * 更新文章 - PUT /api/articles/{id}
     */
    @PutMapping("/{id}")
    public ResponseEntity<Article> updateArticle(
            @PathVariable String id, 
            @RequestBody Article updatedArticle) {
        try {
            Article article = articleService.updateArticle(id, updatedArticle);
            return ResponseEntity.ok(article);
        } catch (RuntimeException e) {
            return ResponseEntity.notFound().build();
        } catch (Exception e) {
            log.error("更新文章失败: {}", id, e);
            return ResponseEntity.badRequest().build();
        }
    }
}

三、高可用部署实战

3.1 副本集Docker部署:生产级环境搭建

yaml 复制代码
# docker-compose-replica.yml
version: '3.8'
services:
  mongodb-primary:
    image: mongo:6.0
    container_name: mongodb-primary
    command: mongod --replSet rs0 --bind_ip_all
    ports:
      - "27017:27017"
    environment:
      MONGO_INITDB_ROOT_USERNAME: admin
      MONGO_INITDB_ROOT_PASSWORD: password
    volumes:
      - mongo_primary_data:/data/db
      - mongo_primary_config:/data/configdb
    networks:
      - mongo-cluster

  mongodb-secondary:
    image: mongo:6.0
    container_name: mongodb-secondary
    command: mongod --replSet rs0 --bind_ip_all
    environment:
      MONGO_INITDB_ROOT_USERNAME: admin
      MONGO_INITDB_ROOT_PASSWORD: password
    volumes:
      - mongo_secondary_data:/data/db
      - mongo_secondary_config:/data/configdb
    networks:
      - mongo-cluster

  mongodb-arbiter:
    image: mongo:6.0
    container_name: mongodb-arbiter
    command: mongod --replSet rs0 --bind_ip_all
    volumes:
      - mongo_arbiter_data:/data/db
    networks:
      - mongo-cluster

volumes:
  mongo_primary_data:
  mongo_primary_config:
  mongo_secondary_data:
  mongo_secondary_config:
  mongo_arbiter_data:

networks:
  mongo-cluster:
    driver: bridge

初始化副本集:

bash 复制代码
# 进入主节点容器
docker exec -it mongodb-primary mongosh -u admin -p password

# 初始化副本集
rs.initiate({
  _id: "rs0",
  members: [
    { _id: 0, host: "mongodb-primary:27017" },
    { _id: 1, host: "mongodb-secondary:27017" },
    { _id: 2, host: "mongodb-arbiter:27017", arbiterOnly: true }
  ]
})

3.2 SpringBoot生产环境配置

java 复制代码
/**
 * 生产环境MongoDB配置
 * 优化连接池和读写策略
 */
@Configuration
@Profile("prod")
public class ProductionMongoConfig {
    
    /**
     * 生产环境MongoTemplate配置
     * 支持副本集自动故障转移
     */
    @Bean
    @Primary
    public MongoTemplate mongoTemplate() {
        String connectionString = "mongodb://admin:password@mongodb-primary:27017,"
                + "mongodb-secondary:27017/paoxiang?replicaSet=rs0"
                + "&authSource=admin"
                + "&readPreference=primaryPreferred"  // 读写分离策略
                + "&retryWrites=true"                // 自动重试写操作
                + "&w=majority";                     // 写确认级别
        
        MongoClientSettings settings = MongoClientSettings.builder()
                .applyConnectionString(new ConnectionString(connectionString))
                .applyToConnectionPoolSettings(builder -> 
                    builder.maxSize(100)                    // 最大连接数
                           .minSize(10)                     // 最小连接数
                           .maxWaitTime(2000, TimeUnit.MILLISECONDS)
                           .maxConnectionLifeTime(30, TimeUnit.MINUTES)
                           .maxConnectionIdleTime(10, TimeUnit.MINUTES))
                .applyToSocketSettings(builder ->
                    builder.connectTimeout(2000, TimeUnit.MILLISECONDS)
                           .readTimeout(5000, TimeUnit.MILLISECONDS))
                .build();
        
        return new MongoTemplate(MongoClients.create(settings), "paoxiang");
    }
    
    /**
     * 健康检查配置
     */
    @Bean
    public MongoHealthIndicator mongoHealthIndicator(MongoTemplate mongoTemplate) {
        return new MongoHealthIndicator(mongoTemplate);
    }
}

四、向量数据库与AI集成

4.1 向量搜索服务:语义搜索的实现

java 复制代码
/**
 * 向量搜索服务 - 支持AI语义搜索
 * 集成OpenAI Embedding和MongoDB向量索引
 */
@Service
@Slf4j
public class VectorSearchService {
    
    @Autowired
    private MongoTemplate mongoTemplate;
    
    @Value("${openai.api.key}")
    private String openaiApiKey;
    
    /**
     * 创建向量索引 - 支持相似度搜索
     * 索引类型:HNSW(Hierarchical Navigable Small World)
     * 相似度度量:余弦相似度
     */
    public void createVectorIndex() {
        try {
            Document command = new Document()
                .append("createIndexes", "articles")
                .append("indexes", List.of(new Document()
                    .append("name", "vector_search_index")
                    .append("key", new Document("embedding", "vector"))
                    .append("vectorOptions", new Document()
                        .append("similarity", "cosine")  // 余弦相似度
                        .append("dimensions", 1536)      // OpenAI embedding维度
                        .append("type", "hnsw"))));      // 近似最近邻算法
            
            mongoTemplate.getDb().runCommand(command);
            log.info("向量索引创建成功");
        } catch (Exception e) {
            log.error("创建向量索引失败", e);
        }
    }
    
    /**
     * 生成文本向量 - 调用OpenAI Embedding API
     */
    public List<Double> generateEmbedding(String text) {
        try {
            // 构建OpenAI API请求
            RestTemplate restTemplate = new RestTemplate();
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON);
            headers.setBearerAuth(openaiApiKey);
            
            Map<String, Object> request = Map.of(
                "model", "text-embedding-ada-002",
                "input", text
            );
            
            HttpEntity<Map<String, Object>> entity = new HttpEntity<>(request, headers);
            
            // 调用API并解析响应
            ResponseEntity<Map> response = restTemplate.postForEntity(
                "https://api.openai.com/v1/embeddings", 
                entity, 
                Map.class
            );
            
            if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {
                List<Map<String, Object>> data = (List<Map<String, Object>>) response.getBody().get("data");
                if (!data.isEmpty()) {
                    return (List<Double>) data.get(0).get("embedding");
                }
            }
            
            log.error("生成向量失败: {}", response.getBody());
            return Collections.emptyList();
            
        } catch (Exception e) {
            log.error("调用OpenAI API失败", e);
            return Collections.emptyList();
        }
    }
    
    /**
     * 向量相似度搜索 - 核心搜索功能
     */
    public List<Article> vectorSearch(String query, int limit) {
        // 生成查询向量
        List<Double> queryVector = generateEmbedding(query);
        if (queryVector.isEmpty()) {
            return Collections.emptyList();
        }
        
        try {
            // 构建向量搜索聚合管道
            List<Document> pipeline = List.of(
                new Document("$vectorSearch", new Document()
                    .append("index", "vector_search_index")
                    .append("path", "embedding")
                    .append("queryVector", queryVector)
                    .append("numCandidates", limit * 10)  // 候选集大小
                    .append("limit", limit)),
                new Document("$project", new Document()
                    .append("title", 1)
                    .append("content", 1)
                    .append("author", 1)
                    .append("similarityScore", new Document("$meta", "vectorSearchScore")))
            );
            
            // 执行聚合查询
            AggregateIterable<Document> results = mongoTemplate.getCollection("articles")
                .aggregate(pipeline);
            
            // 转换结果
            List<Article> articles = new ArrayList<>();
            for (Document doc : results) {
                Article article = mongoTemplate.getConverter().read(Article.class, doc);
                articles.add(article);
            }
            
            log.info("向量搜索完成,查询: {}, 结果数: {}", query, articles.size());
            return articles;
            
        } catch (Exception e) {
            log.error("向量搜索失败", e);
            return Collections.emptyList();
        }
    }
}

4.2 RAG架构实现:检索增强生成

java 复制代码
/**
 * RAG(检索增强生成)服务
 * 结合MongoDB向量搜索和LLM生成能力
 * 实现智能问答系统
 */
@Service
@Slf4j
public class RAGService {
    
    @Autowired
    private VectorSearchService vectorSearchService;
    
    @Value("${openai.api.key}")
    private String openaiApiKey;
    
    /**
     * RAG问答流程 - 核心业务方法
     * 1. 检索相关文档
     * 2. 构建上下文
     * 3. 调用LLM生成答案
     */
    public RAGResponse answerQuestion(String question) {
        log.info("RAG问答处理: {}", question);
        
        // 1. 检索相关文档
        List<Article> relevantDocs = vectorSearchService.vectorSearch(question, 5);
        if (relevantDocs.isEmpty()) {
            return new RAGResponse("抱歉,没有找到相关信息。", Collections.emptyList());
        }
        
        // 2. 构建上下文
        String context = buildContext(relevantDocs);
        
        // 3. 调用LLM生成答案
        String answer = generateAnswerWithLLM(question, context);
        
        // 4. 返回结果和引用来源
        return new RAGResponse(answer, relevantDocs);
    }
    
    /**
     * 构建检索上下文 - 格式化相关文档
     */
    private String buildContext(List<Article> articles) {
        StringBuilder context = new StringBuilder();
        context.append("参考文档:\n\n");
        
        for (int i = 0; i < articles.size(); i++) {
            Article article = articles.get(i);
            context.append(String.format("文档[%d]: 《%s》\n", i + 1, article.getTitle()));
            
            // 截取内容,避免上下文过长
            String content = article.getContent();
            if (content.length() > 500) {
                content = content.substring(0, 500) + "...";
            }
            context.append(String.format("内容: %s\n\n", content));
        }
        
        return context.toString();
    }
    
    /**
     * 调用LLM生成答案 - 集成OpenAI GPT
     */
    private String generateAnswerWithLLM(String question, String context) {
        try {
            RestTemplate restTemplate = new RestTemplate();
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON);
            headers.setBearerAuth(openaiApiKey);
            
            // 构建提示词
            String prompt = String.format(
                "基于以下参考文档回答问题。\n\n" +
                "参考文档:\n%s\n\n" +
                "问题: %s\n\n" +
                "要求:\n" +
                "1. 基于参考文档回答,不要编造信息\n" +
                "2. 如果参考文档中没有相关信息,请明确说明\n" +
                "3. 回答要简洁明了,突出重点\n" +
                "4. 可以引用文档编号[1],[2]等指明信息来源\n\n" +
                "回答:", context, question);
            
            Map<String, Object> request = Map.of(
                "model", "gpt-3.5-turbo",
                "messages", List.of(Map.of("role", "user", "content", prompt)),
                "max_tokens", 1000,
                "temperature", 0.3
            );
            
            HttpEntity<Map<String, Object>> entity = new HttpEntity<>(request, headers);
            
            ResponseEntity<Map> response = restTemplate.postForEntity(
                "https://api.openai.com/v1/chat/completions", 
                entity, 
                Map.class
            );
            
            if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {
                Map<String, Object> choices = ((List<Map<String, Object>>) response.getBody().get("choices")).get(0);
                Map<String, String> message = (Map<String, String>) choices.get("message");
                return message.get("content");
            }
            
            return "生成回答时发生错误。";
            
        } catch (Exception e) {
            log.error("调用OpenAI API失败", e);
            return "服务暂时不可用,请稍后重试。";
        }
    }
    
    /**
     * RAG响应DTO
     */
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class RAGResponse {
        private String answer;
        private List<Article> references;
        private LocalDateTime timestamp;
        private String question;
        
        public RAGResponse(String answer, List<Article> references) {
            this.answer = answer;
            this.references = references;
            this.timestamp = LocalDateTime.now();
        }
    }
}

五、性能优化与监控

5.1 索引优化策略:查询性能的关键

索引设计原则:

  • ESR规则:相等(Equality) -> 排序(Sort) -> 范围(Range)
  • 选择性原则:高选择性字段优先
  • 覆盖查询:索引包含所有查询字段
java 复制代码
/**
 * 索引管理服务
 * 自动化索引创建和维护
 */
@Service
@Slf4j
public class IndexManagementService {
    
    @Autowired
    private MongoTemplate mongoTemplate;
    
    /**
     * 创建复合索引 - 优化复杂查询
     */
    public void createCompoundIndexes() {
        try {
            // 日期+浏览量复合索引
            mongoTemplate.indexOps(Article.class).ensureIndex(
                new Index().on("publishDate", Sort.Direction.DESC)
                          .on("viewCount", Sort.Direction.DESC)
                          .named("date_views_idx")
            );
            
            // 作者+日期复合索引
            mongoTemplate.indexOps(Article.class).ensureIndex(
                new Index().on("author.name", Sort.Direction.ASC)
                          .on("publishDate", Sort.Direction.DESC)
                          .named("author_date_idx")
            );
            
            // 标签+日期复合索引
            mongoTemplate.indexOps(Article.class).ensureIndex(
                new Index().on("tags", Sort.Direction.ASC)
                          .on("publishDate", Sort.Direction.DESC)
                          .named("tags_date_idx")
            );
            
            log.info("复合索引创建完成");
        } catch (Exception e) {
            log.error("创建复合索引失败", e);
        }
    }
    
    /**
     * 创建TTL索引 - 自动清理过期数据
     */
    public void createTTLIndex() {
        mongoTemplate.indexOps("user_sessions").ensureIndex(
            new Index().on("lastAccessed", Sort.Direction.ASC)
                      .expire(3600, TimeUnit.SECONDS) // 1小时后过期
                      .named("ttl_idx")
        );
    }
    
    /**
     * 获取索引使用统计
     */
    public void printIndexStats() {
        Document stats = mongoTemplate.getDb()
            .runCommand(new Document("collStats", "articles"));
        
        log.info("集合统计: {}", stats.toJson());
        
        // 获取索引使用情况
        Document indexStats = mongoTemplate.getDb()
            .runCommand(new Document("aggregate", "articles")
                .append("pipeline", List.of(
                    new Document("$indexStats", new Document()),
                    new Document("$sort", new Document("accesses.ops", -1))
                ))
                .append("cursor", new Document()));
        
        log.info("索引使用统计: {}", indexStats.toJson());
    }
}

5.2 连接池与监控配置

java 复制代码
/**
 * MongoDB连接池优化配置
 * 生产环境性能调优
 */
@Configuration
@Profile("prod")
public class MongoConnectionPoolConfig {
    
    @Bean
    public MongoClientSettings mongoClientSettings() {
        return MongoClientSettings.builder()
            .applyToConnectionPoolSettings(builder -> 
                builder.maxSize(100)                    // 最大连接数
                       .minSize(10)                     // 最小连接数
                       .maxWaitTime(2000, TimeUnit.MILLISECONDS)
                       .maxConnectionLifeTime(30, TimeUnit.MINUTES)
                       .maxConnectionIdleTime(10, TimeUnit.MINUTES)
                       .maintenanceFrequency(60, TimeUnit.SECONDS)  // 维护频率
                       .maintenanceInitialDelay(0, TimeUnit.SECONDS))
            .applyToSocketSettings(builder ->
                builder.connectTimeout(2000, TimeUnit.MILLISECONDS)
                       .readTimeout(5000, TimeUnit.MILLISECONDS))
            .applyToClusterSettings(builder ->
                builder.serverSelectionTimeout(30000, TimeUnit.MILLISECONDS)
                       .localThreshold(15, TimeUnit.MILLISECONDS))
            .applyToServerSettings(builder ->
                builder.heartbeatFrequency(10000, TimeUnit.MILLISECONDS)
                       .minHeartbeatFrequency(500, TimeUnit.MILLISECONDS))
            .build();
    }
    
    /**
     * 监控配置 - 集成Spring Boot Actuator
     */
    @Bean
    public MongoHealthIndicator mongoHealthIndicator(MongoTemplate mongoTemplate) {
        return new MongoHealthIndicator(mongoTemplate);
    }
    
    /**
     * 慢查询监控
     */
    @EventListener(ApplicationReadyEvent.class)
    public void enableSlowQueryLogging() {
        // 设置慢查询阈值为100ms
        mongoTemplate.getDb().runCommand(
            new Document("profile", 2)  // 开启所有操作 profiling
            .append("slowms", 100)      // 慢查询阈值100ms
        );
    }
}

📌 关注「跑享网」,获取更多大数据架构设计和实战调优干货!

🚀 精选内容推荐:

💥 【本期热议话题】

"SpringBoot + MongoDB能否成为微服务架构的数据存储终极解决方案?在AI时代,传统ORM框架是否已经过时?"

现代应用开发技术栈选择面临新的挑战!在你的项目中,技术选型的核心考量是什么?

  • 是SpringBoot + MongoDB派? 看重开发效率与灵活数据模型,相信文档数据库能应对业务快速变化?
  • 是传统SQL派? 坚持事务一致性和复杂关联查询,认为关系型数据库仍是企业级应用基石?
  • 是多模数据库派? 拥抱多样化数据存储,根据不同场景选择最合适的数据库技术?

这场技术架构之争,你怎么看?欢迎在评论区留下你的:

  1. 微服务架构中的数据存储选型经验和核心理由
  2. 在MongoDB实践中遇到的最深刻挑战和解决方案
  3. 对AI时代数据库技术发展的预测和期待

觉得这篇SpringBoot + MongoDB深度实战对你有帮助?点赞、收藏、转发三连,帮助更多技术小伙伴成长!

#SpringBoot #MongoDB #向量数据库 #AI集成 #微服务架构 #云原生 #数据存储选型 #架构设计 #Java开发 #技术实战

相关推荐
云和数据.ChenGuang3 小时前
MongoDB 认证失败(错误码 18)
数据库·mongodb
文火冰糖的硅基工坊3 小时前
[人工智能-综述-18]:AI重构千行百业的技术架构
大数据·人工智能·重构·架构·系统架构·制造·产业链
whltaoin4 小时前
Spring Boot 常用注解分类整理(含用法示例)
java·spring boot·后端·注解·开发技巧
稚辉君.MCA_P8_Java4 小时前
Git 基础 - 查看提交历史
spring boot·git·微服务·云原生·kubernetes
不会算法的小灰4 小时前
Spring Boot 实现邮件发送功能:整合 JavaMailSender 与 FreeMarker 模板
java·spring boot·后端
老赵聊算法、大模型备案5 小时前
2025年6-8月中国大模型备案分析报告
大数据·人工智能·安全·语言模型·aigc
民乐团扒谱机5 小时前
PCA 主成分分析:数据世界的 “旅行清单整理师”—— 从 30 维杂乱到 2 维清晰的诗意降维
大数据·数学建模·matlab·pca·主成分分析·数据处理·降维
come112345 小时前
从PHP到Spring Boot:思维的转变与入门实战 (指南二)
开发语言·spring boot·php
小咕聊编程5 小时前
【含文档+PPT+源码】基于SpringBoot+Vue的停车场管理系统
vue.js·spring boot·后端·毕业设计·停车场