MongoDB 学习笔记

1. MongoDB 与传统关系型数据库的区别?

MongoDB 是一种 NoSQL 的文档型数据库,和传统关系型数据库(如 MySQL、PostgreSQL)在以下几个方面存在显著差异:

    1. 数据模型
      MongoDB:以 BSON 文档形式存储数据,结构灵活,字段可以嵌套对象和数组,天然适合非结构化或半结构化数据。

关系型数据库:采用表结构,字段和类型固定,严格遵守行与列的二维模型,数据之间通过外键等方式关联。

    1. 模式(Schema)
      MongoDB:默认是 schema-less(无固定模式),可以存储结构不同的文档;也可以通过 JSON Schema 进行约束。

关系型数据库:必须先定义表结构(字段、类型、约束等),插入数据必须符合表定义。

    1. 查询语言
      MongoDB:使用类 JSON 的查询语法,操作方式更接近编程对象操作。

关系型数据库:使用标准的 SQL 语言,具备强大的 JOIN、子查询、事务等能力。

2. MongoDB 的数据模型和基本结构?

MongoDB 是一个基于文档的 NoSQL 数据库,核心的数据模型是「数据库 → 集合 → 文档」的三层结构:

    • 数据库(Database):就像关系型数据库中的一个库。
    • 集合(Collection):类似表,但不需要提前定义字段结构,可以存储结构不同的文档。
    • 文档(Document):是实际存储的数据单元,格式是类 JSON 的 BSON,可以包含嵌套对象和数组。

每个文档默认有一个 _id 主键字段,类型是 ObjectId,也可以自定义。文档中的字段不要求统一,MongoDB 的 schema 是灵活的。

MongoDB 的集合可以隐式创建,也可以使用 db.createCollection() 显式创建;一般情况下是自动创建的,但如果需要添加 schema 验证或者配置固定集合(如 capped collection),就会使用显式方式。

这种模型非常适合处理非结构化、变化频繁的数据,比如内容管理系统、用户行为日志等场景。

3. MongoDB 如何实现主键自增?

MongoDB 默认使用 ObjectId 作为 _id 主键,是全局唯一但不自增。如果需要自增主键,可以在应用层用 Redis ,UUID 或雪花算法生成唯一 ID。

4. MongoDB 如何实现索引?常见索引类型有哪些?

MongoDB 支持多种索引类型,包括单字段、复合索引、唯一索引、文本索引、TTL索引、地理空间索引等。我们可以通过 createIndex() 创建索引,通过 explain() 查看是否命中。使用索引可以大幅提升查询性能,但也会带来写入开销,需要权衡使用。

  • 索引会加快读取,但会增加写入开销(写时需要更新索引);
  • 应避免滥用索引,尤其是重复索引、低选择性字段索引;
  • 查询语句应尽量命中索引前缀(尤其是复合索引);
  • 可通过 explain() 分析查询是否使用了索引;
  • MongoDB 的 _id 字段默认自带唯一索引。

5. MongoDB 如何进行聚合查询?

使用 Spring Data MongoDB 的 Aggregation API

复制代码
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;

@Service
public class SearchHistoryService {

    @Autowired
    private MongoTemplate mongoTemplate;

    public List<UserSearchCount> aggregateSearchCountsLast7Days() {
        Date sevenDaysAgo = new Date(System.currentTimeMillis() - 7L * 24 * 60 * 60 * 1000);

        Aggregation aggregation = Aggregation.newAggregation(
                Aggregation.match(org.springframework.data.mongodb.core.query.Criteria.where("createdAt").gte(sevenDaysAgo)),
                Aggregation.group("userId").count().as("count"),
                Aggregation.sort(org.springframework.data.domain.Sort.Direction.DESC, "count"),
                Aggregation.limit(10)
        );

        AggregationResults<UserSearchCount> results = mongoTemplate.aggregate(aggregation, "searchHistory", UserSearchCount.class);
        return results.getMappedResults();
    }

    public static class UserSearchCount {
        private String userId;
        private int count;

        // getters and setters

        public String getUserId() {
            return userId;
        }
        public void setUserId(String userId) {
            this.userId = userId;
        }
        public int getCount() {
            return count;
        }
        public void setCount(int count) {
            this.count = count;
        }
    }
}

6. MongoDB 是否支持事务?实现机制是什么?

MongoDB 自 4.0 起支持多文档 ACID 事务,基于客户端会话和 WiredTiger 的 MVCC 实现,支持副本集和分片集群环境。分布式事务采用两阶段提交协议,保证跨分片操作的原子性和一致性。事务使用时需启动会话,写操作提交或回滚,性能开销较大,应慎重使用。

7. MongoDB 如何实现高可用?

MongoDB 通过副本集实现高可用,副本集包含主节点和多个从节点,主节点写入数据后同步到从节点;当主节点故障时,自动通过选举产生新的主节点,实现自动故障转移。结合分片集群,可以实现数据的高可用和水平扩展。

8. MongoDB 如何做分片?分片的原理是什么?

MongoDB 分片机制类似 Kafka 的分区概念,都是将数据划分为多个分区(分片),但 MongoDB 的分片是针对 collection 的数据做水平切分,每个分片本身是一个副本集,保证高可用。客户端通过 mongos 路由器把请求按 shard key 路由到对应分片,实现分布式存储和负载均衡。

|------------|-------------------------------|-----------------------------------|
| 方面 | MongoDB 分片(Sharding) | Kafka 分区(Partition) |
| 划分粒度 | 一个数据库中的每个 collection 可以分成多个分片 | 一个 topic 可以划分成多个分区 |
| 分片含义 | 分片是数据水平切分,分片内存储部分数据 | 分区是消息队列的基本单位,每个分区存储部分消息 |
| 副本机制 | 每个分片本身是一个副本集,分片有主节点和多个从节点 | 分区有一个 leader(主节点)和多个 follower(副本) |
| 数据分布方式 | 根据 shard key(分片键)决定数据路由到哪个分片 | 生产者根据 key 决定消息写入哪个分区 |
| 扩展性 | 通过增加分片节点实现水平扩展,分片数目固定不轻易变动 | 分区数在 topic 创建时定义,运行中不易变 |
| 主要目的 | 解决数据量大、读写压力高的问题,实现大规模分布式存储 | 实现高吞吐、高可用的消息传递 |

9. MongoDB 性能优化常见措施有哪些?

1.合理设计索引

建立常用查询字段的索引,避免全表扫描

利用复合索引覆盖多个查询条件

使用唯一索引保证数据唯一性和快速定位

避免索引过多,防止写入性能下降

利用TTL 索引自动过期数据,减小数据量

2.集群与硬件层面优化

使用副本集实现读写分离,读取从节点,减轻主节点压力

合理配置分片集群,实现水平扩展和负载均衡

3.优化查询

避免使用不支持索引的查询操作,如正则表达式前缀不确定的查询

使用 explain() 分析查询执行计划,发现慢查询

避免在大文档里返回不必要的字段,使用 投影(projection)减少数据传输

合理分页,避免大偏移量分页带来的性能损耗,推荐基于条件的"游标"分页

10. MongoDB 和 Elasticsearch 如何配合使用?

MongoDB 负责存储,Elasticsearch 负责搜索,通过同步机制保持数据一致,实现高性能、高可用的搜索架构。

典型流程示例:

用户发起搜索请求

前端将搜索关键词发送到后端。

ES 执行全文检索

后端调用 ES,快速定位相关文档(例如文章、商品等),并返回匹配结果。

MongoDB 记录搜索历史

将用户搜索关键词、时间戳、用户ID 等信息异步写入 MongoDB,方便后续做热搜、个性化推荐等。

MySQL 作为主数据源

存储业务核心数据,如用户信息、订单、权限等,确保数据完整性和事务一致。

方式 1:应用层双写(同步写入)

  • 代码中同时写入 MongoDB 和 Elasticsearch。

  • 优点:实现简单。

  • 缺点:耦合高、失败处理复杂。

    articleRepository.save(article); // MongoDB
    esClient.index(articleDoc); // Elasticsearch

方式 2:异步同步(推荐)

  • 写入 MongoDB 后发送事件(Kafka、RabbitMQ、RocketMQ)

  • 消费者异步同步数据到 Elasticsearch。

  • 优点:解耦、性能好、可靠性强。

    用户发请求 → MongoDB 存数据 → 发送同步事件 → 消费者写入 Elasticsearch

11. 搜索框的历史搜索记录,用 MongoDB 存储有什么好处?

  1. MongoDB 支持灵活字段、嵌套对象和数组,无需修改表结构即可扩展字段,开发迭代效率高。

  2. MongoDB 的写入性能优于传统关系型数据库,特别适合高并发、高速写入场景。搜索记录属于典型的"写多读少。

  3. MongoDB 支持 TTL 索引,可以自动删除过期数据,免维护清理脚本

    db.searchHistory.createIndex(
    { createdAt: 1 }, // 对 createdAt 字段创建升序索引
    { expireAfterSeconds: 2592000 } // 设置文档在 createdAt 时间点后 2592000 秒自动过期(30 天)
    )

12. 如何在项目中使用 MongoDB?

  1. 添加依赖(Maven)

    <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-mongodb</artifactId> </dependency>
  2. 配置 application.properties

    spring.data.mongodb.uri=mongodb://localhost:27017/mydb

  3. 定义实体类

    import org.springframework.data.annotation.Id;
    import org.springframework.data.mongodb.core.mapping.Document;

    @Document(collection = "users")
    public class User {
    @Id
    private String id;
    private String name;
    private Integer age;

    复制代码
     // getters and setters

    }

  4. 定义 Repository 接口

    import org.springframework.data.mongodb.repository.MongoRepository;

    public interface UserRepository extends MongoRepository<User, String> {
    User findByName(String name);
    }

  5. 使用示例

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;

    @Service
    public class UserService {
    @Autowired
    private UserRepository userRepository;

    复制代码
     public void runExample() {
         User user = new User();
         user.setName("Alice");
         user.setAge(25);
         userRepository.save(user);
    
         User found = userRepository.findByName("Alice");
         System.out.println(found.getName() + ", " + found.getAge());
     }

    }

相关推荐
devops_sre7 分钟前
mongodb原理及其实现
数据库·mongodb
笑衬人心。1 小时前
Hashtable 与 HashMap 的区别笔记
java·数据结构·笔记
金心靖晨1 小时前
消息中间件优化高手笔记
java·数据库·笔记
鼠鼠我捏,要死了捏2 小时前
MongoDB性能优化实战指南:原理、实践与案例
数据库·mongodb·性能优化
心疼你的一切2 小时前
Unity 多人游戏框架学习系列一
学习·游戏·unity·c#·游戏引擎
__只是为了好玩__2 小时前
MongoDB 数据库 启用访问控制
数据库·mongodb
Chef_Chen3 小时前
从0开始学习R语言--Day47--Nomogram
学习
Pi_Qiu_3 小时前
Python初学者笔记第十三期 -- (常用内置函数)
java·笔记·python
毕设源码柳学姐5 小时前
计算机毕业设计Java医学生在线学习平台系统 基于 Java 的医学生在线学习平台设计与开发 Java 医学在线教育学习系统的设计与实现
java·学习·课程设计
不过普通话一乙不改名5 小时前
扩展:操作系统之高性能网络计算
网络·笔记