MongoDB 概述 什么是 MongoDB MongoDB 是一个基于分布式文件存储的开源数据库系统, 由 C++ 编写而成。 它将数据存储为一个文档, 数据结构由键值对组成, 类似于 JSON 对象。
核心功能
文档存储: 以 BSON 格式存储数据
灵活模式: 无需预定义表结构
水平扩展: 支持分片集群
丰富查询: 支持复杂的查询和聚合
主要优势
高性能: 内存映射引擎, 读写速度快
高可用: 副本集自动故障转移
易扩展: 原生支持分片
灵活性强: 动态 Schema, 易于迭代
MongoDB 的核心概念 基本概念
Database( 数据库)
相当于关系数据库中的数据库
一个 MongoDB 服务器可以有多个数据库
Collection( 集合)
Document( 文档)
相当于关系数据库中的行
最小的数据单元, 以 BSON 格式存储
Field( 字段)
Index( 索引)
_id
架构组件
mongod
MongoDB 服务器进程
处理数据请求和管理数据
mongos
mongo shell
交互式 JavaScript 接口
用于管理和管理数据
MongoDB 的工作原理 写入流程 1 2 3 4 5 1. 客户端发送写请求到 mongod 2. mongod 将操作写入 Journal( 预写日志) 3. 更新内存中的数据 4. 定期将内存数据刷新到磁盘 5. 返回结果给客户端
读取流程 1 2 3 4 5 1. 客户端发送读请求 2. mongod 检查内存中是否有数据 3. 如果有, 直接返回 4. 如果没有, 从磁盘读取到内存 5. 返回结果给客户端
环境搭建 安装 MongoDB Docker 安装( 推荐) 1 2 3 4 5 6 7 8 9 10 11 12 docker pull mongo:6.0 docker run -d \ --name mongodb \ -p 27017:27017 \ -v /path/to/data:/data/db \ mongo:6.0 docker exec -it mongodb mongosh
Linux 安装 1 2 3 4 5 6 7 8 9 10 11 sudo apt-get install -y mongodb-org sudo yum install -y mongodb-org sudo systemctl start mongod sudo systemctl enable mongod
Windows 安装
下载地址: https://www.mongodb.com/try/download/community
步骤:
下载 MSI 安装包
运行安装程序
选择安装路径和数据目录
启动 MongoDB 服务
macOS 安装 1 2 3 4 5 6 7 8 9 brew tap mongodb/brew brew install mongodb-community@6.0 brew services start mongodb-community@6.0 brew services stop mongodb-community@6.0
配置文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 storage: dbPath: /var/lib/mongodb journal: enabled: true systemLog: destination: file logAppend: true path: /var/log/mongodb/mongod.log net: port: 27017 bindIp: 127.0 .0 .1 security: authorization: enabled
验证安装 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 mongosh mongo db.version() show dbs use test db.users.insertOne({name: "张三" , age: 25}) db.users.find()
基本操作 数据库操作 创建/切换数据库 1 2 3 4 5 6 7 8 9 10 11 use mydb db show dbs db.dropDatabase ()
注意事项 1 2 3 4 细节注意: 1. use 命令不会立即创建数据库 2. 只有在插入数据后才会真正创建 3. 数据库名区分大小写
集合操作 创建集合 1 2 3 4 5 6 7 8 9 10 11 12 db.createCollection ("users" ) db.createCollection ("logs" , { capped : true , size : 10485760 , max : 1000 }) db.users .insertOne ({name : "李四" })
查看集合 1 2 3 4 5 6 7 8 show collections db.users .stats () db.users .dataSize ()
删除集合
文档操作 插入文档 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 db.users .insertOne ({ name : "张三" , age : 25 , email : "zhangsan@example.com" , hobbies : ["读书" , "游泳" ], address : { city : "北京" , street : "长安街" } }) db.users .insertMany ([ {name : "李四" , age : 30 }, {name : "王五" , age : 28 }, {name : "赵六" , age : 35 } ]) db.users .insertOne ({ _id : 1 , name : "自定义ID" })
查询文档 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 db.users .find () db.users .find ().pretty () db.users .find ({age : 25 }) db.users .find ({age : 25 , name : "张三" }) db.users .find ({age : {$gte : 20 , $lte : 30 }}) db.users .find ().limit (10 ) db.users .find ().skip (10 ) db.users .find ().sort ({age : 1 }) db.users .find ().sort ({age : -1 }) db.users .find ({}, {name : 1 , age : 1 , _id : 0 })
更新文档 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 db.users .updateOne ( {name : "张三" }, {$set : {age : 26 }} ) db.users .updateMany ( {age : {$lt : 30 }}, {$set : {status : "young" }} ) db.users .replaceOne ( {name : "张三" }, {name : "张三丰" , age : 100 } ) $set $unset $inc $push $pull $addToSet
删除文档 1 2 3 4 5 6 7 8 db.users .deleteOne ({name : "张三" }) db.users .deleteMany ({age : {$lt : 20 }}) db.users .deleteMany ({})
查询操作符 比较操作符
操作符
说明
示例
$eq
等于
{age: {$eq: 25}}
$ne
不等于
{age: {$ne: 25}}
$gt
大于
{age: {$gt: 25}}
$gte
大于等于
{age: {$gte: 25}}
$lt
小于
{age: {$lt: 25}}
$lte
小于等于
{age: {$lte: 25}}
$in
在数组中
{age: {$in: [20, 25, 30]}}
$nin
不在数组中
{age: {$nin: [20, 25, 30]}}
逻辑操作符 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 db.users .find ({age : 25 , name : "张三" }) db.users .find ({ $or : [ {age : 25 }, {name : "张三" } ] }) db.users .find ({ $nor : [ {age : 25 }, {name : "张三" } ] }) db.users .find ({ age : {$not : {$gt : 25 }} })
元素操作符 1 2 3 4 5 6 7 8 9 db.users .find ({email : {$exists : true }}) db.users .find ({age : {$type : "number" }}) db.users .find ({name : {$regex : /^张/ }}) db.users .find ({name : /张$/ })
数组操作符 1 2 3 4 5 6 7 8 9 10 11 db.users .find ({hobbies : "读书" }) db.users .find ({hobbies : {$size : 3 }}) db.users .find ({scores : {$all : [80 , 90 ]}}) db.users .find ({scores : {$elemMatch : {$gte : 90 }}})
索引 创建索引 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 db.users .createIndex ({name : 1 }) db.users .createIndex ({name : 1 , age : -1 }) db.users .createIndex ({email : 1 }, {unique : true }) db.users .createIndex ({email : 1 }, {sparse : true }) db.logs .createIndex ({createdAt : 1 }, {expireAfterSeconds : 3600 }) db.articles .createIndex ({content : "text" })
查看索引 1 2 3 4 5 db.users .getIndexes () db.users .totalIndexSize ()
删除索引 1 2 3 4 5 db.users .dropIndex ({name : 1 }) db.users .dropIndexes ()
索引类型
类型
说明
适用场景
单字段索引
单个字段的索引
单字段查询
复合索引
多个字段的索引
多字段查询
多键索引
数组字段的索引
数组查询
地理空间索引
地理位置索引
位置查询
文本索引
全文搜索索引
文本搜索
哈希索引
哈希值索引
分片键
聚合管道 基本聚合 1 2 3 4 5 6 7 db.orders .aggregate ([ {$group : { _id : "$customerId" , totalAmount : {$sum : "$amount" } }} ])
常用阶段
阶段
说明
示例
$match
过滤文档
{$match: {status: "A"}}
$group
分组聚合
{$group: {_id: "$dept", count: {$sum: 1}}}
$project
投影字段
{$project: {name: 1, age: 1}}
$sort
排序
{$sort: {age: -1}}
$limit
限制数量
{$limit: 10}
$skip
跳过记录
{$skip: 10}
$unwind
展开数组
{$unwind: "$tags"}
$lookup
关联查询
{$lookup: {...}}
复杂聚合示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 db.orders .aggregate ([ {$match : { orderDate : { $gte : new Date ("2024-01-01" ), $lt : new Date ("2024-12-31" ) } }}, {$unwind : "$items" }, {$group : { _id : { year : {$year : "$orderDate" }, month : {$month : "$orderDate" }, product : "$items.productName" }, totalQuantity : {$sum : "$items.quantity" }, totalAmount : {$sum : {$multiply : ["$items.price" , "$items.quantity" ]}} }}, {$sort : {totalAmount : -1 }}, {$limit : 10 } ])
聚合表达式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 $sum, $avg, $min, $max $concat, $substr, $toLower, $toUpper $year, $month, $day, $hour $cond, $ifNull, $switch $size, $slice, $filter
Spring Boot 整合 添加依赖 1 2 3 4 5 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-data-mongodb</artifactId > </dependency >
配置文件 1 2 3 4 5 6 7 8 9 10 11 spring: data: mongodb: uri: mongodb://localhost:27017/mydb
创建实体类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 package com.example.entity;import lombok.Data;import org.springframework.data.annotation.Id;import org.springframework.data.mongodb.core.mapping.Document;import org.springframework.data.mongodb.core.mapping.Field;import java.time.LocalDateTime;import java.util.List;@Data @Document(collection = "users") public class User { @Id private String id; @Field("name") private String name; private Integer age; private String email; private List<String> hobbies; private Address address; private LocalDateTime createTime; @Data public static class Address { private String city; private String street; } }
创建 Repository 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package com.example.repository;import com.example.entity.User;import org.springframework.data.domain.Page;import org.springframework.data.domain.Pageable;import org.springframework.data.mongodb.repository.MongoRepository;import org.springframework.data.mongodb.repository.Query;import org.springframework.stereotype.Repository;import java.util.List;@Repository public interface UserRepository extends MongoRepository <User, String> { List<User> findByName (String name) ; List<User> findByAgeBetween (Integer minAge, Integer maxAge) ; List<User> findByEmailContaining (String keyword) ; Page<User> findByAgeGreaterThan (Integer age, Pageable pageable) ; @Query("{'name': ?0, 'age': {$gte: ?1}}") List<User> findByNameAndAgeGt (String name, Integer age) ; void deleteByEmail (String email) ; }
创建 Service 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 package com.example.service;import com.example.entity.User;import com.example.repository.UserRepository;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.domain.Page;import org.springframework.data.domain.PageRequest;import org.springframework.data.mongodb.core.MongoTemplate;import org.springframework.data.mongodb.core.query.Criteria;import org.springframework.data.mongodb.core.query.Query;import org.springframework.stereotype.Service;import java.util.List;@Service public class UserService { @Autowired private UserRepository userRepository; @Autowired private MongoTemplate mongoTemplate; public User save (User user) { return userRepository.save(user); } public List<User> saveAll (List<User> users) { return userRepository.saveAll(users); } public User findById (String id) { return userRepository.findById(id).orElse(null ); } public void delete (String id) { userRepository.deleteById(id); } public Page<User> page (int pageNum, int pageSize) { return userRepository.findAll(PageRequest.of(pageNum - 1 , pageSize)); } public List<User> search (String keyword) { Query query = new Query (); query.addCriteria( Criteria.where("name" ).regex(keyword) .orOperator(Criteria.where("email" ).regex(keyword)) ); return mongoTemplate.find(query, User.class); } public List<User> findByAgeRange (Integer minAge, Integer maxAge) { return userRepository.findByAgeBetween(minAge, maxAge); } }
创建 Controller 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 package com.example.controller;import com.example.entity.User;import com.example.service.UserService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.domain.Page;import org.springframework.web.bind.annotation.*;import java.util.List;@RestController @RequestMapping("/users") public class UserController { @Autowired private UserService userService; @PostMapping public User create (@RequestBody User user) { return userService.save(user); } @PostMapping("/batch") public List<User> batchCreate (@RequestBody List<User> users) { return userService.saveAll(users); } @GetMapping("/{id}") public User get (@PathVariable String id) { return userService.findById(id); } @DeleteMapping("/{id}") public void delete (@PathVariable String id) { userService.delete(id); } @GetMapping("/page") public Page<User> page ( @RequestParam(defaultValue = "1") int pageNum, @RequestParam(defaultValue = "10") int pageSize) { return userService.page(pageNum, pageSize); } @GetMapping("/search") public List<User> search (@RequestParam String keyword) { return userService.search(keyword); } }
高级功能 副本集( Replica Set) 配置副本集 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 mongod --replSet rs0 --port 27017 --dbpath /data/rs0-1 mongod --replSet rs0 --port 27018 --dbpath /data/rs0-2 mongod --replSet rs0 --port 27019 --dbpath /data/rs0-3 mongosh --port 27017 rs.initiate({ _id: "rs0" , members: [ {_id: 0, host: "localhost:27017" }, {_id: 1, host: "localhost:27018" }, {_id: 2, host: "localhost:27019" } ] }) rs.status()
特点
自动故障转移
数据冗余备份
读写分离支持
至少需要 3 个节点
分片( Sharding) 配置分片 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 mongod --configsvr --replSet configReplSet --port 27019 mongos --configdb configReplSet/localhost:27019 --port 27017 mongosh --port 27017 sh.addShard("shard1/localhost:27001" ) sh.addShard("shard2/localhost:27002" ) sh.enableSharding("mydb" ) sh.shardCollection("mydb.users" , {userId: "hashed" })
分片策略
策略
说明
适用场景
范围分片
按范围划分数据
有序数据
哈希分片
按哈希值分布
均匀分布
区域分片
按地理位置分布
地域性数据
GridFS( 大文件存储) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @Autowired private GridFsTemplate gridFsTemplate;public String uploadFile (MultipartFile file) throws IOException { GridFSFile gridFSFile = gridFsTemplate.store( file.getInputStream(), file.getOriginalFilename(), file.getContentType() ); return gridFSFile.getId().toString(); } public InputStream downloadFile (String fileId) { GridFSDBFile gridFSFile = gridFsTemplate.findOne( Query.query(Criteria.where("_id" ).is(fileId)) ); return gridFSFile.getInputStream(); }
事务 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 const session = db.getMongo ().startSession ();try { session.startTransaction (); const usersColl = session.getDatabase ("mydb" ).users ; const ordersColl = session.getDatabase ("mydb" ).orders ; usersColl.insertOne ({name : "张三" }); ordersColl.insertOne ({userId : "xxx" , amount : 100 }); session.commitTransaction (); } catch (error) { session.abortTransaction (); } finally { session.endSession (); }
性能优化 索引优化 1 2 3 4 5 6 7 8 9 10 11 12 db.users .createIndex ({name : 1 , age : 1 }) db.users .createIndex ({email : 1 }, {name : "email_idx" }) db.users .find ({name : "张三" }).explain ("executionStats" )
查询优化 1 2 3 4 5 6 7 8 9 10 11 db.users .find ({}, {name : 1 , age : 1 , _id : 0 }) db.users .find ({age : {$gt : 20 }}, {name : 1 }) db.users .find ().limit (100 )
存储优化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 { name : "张三" , address : {city : "北京" , street : "长安街" } } { name : "张三" , orderIds : [ObjectId ("..." ), ObjectId ("..." )] } db.logs .createIndex ({createdAt : 1 }, {expireAfterSeconds : 86400 * 30 })
连接池优化 1 2 3 4 5 spring: data: mongodb: uri: mongodb://localhost:27017/mydb?maxPoolSize=100&minPoolSize=10
最佳实践 命名规范 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 数据库命名: - 小写字母 - 使用下划线 - 语义化命名 - 示例: user_db、 order_system 集合命名: - 复数形式 - 小写字母 - 示例: users、 orders、 products 字段命名: - 驼峰命名 - 语义化命名 - 示例: userName、 createTime
设计建议 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 { name : "张三" , address : { city : "北京" , detail : {street : "长安街" } } } { userId : ObjectId ("..." ), orderId : ObjectId ("..." ) } { createdAt : ISODate (), updatedAt : ISODate (), createdBy : "admin" }
监控建议 1 2 3 4 5 6 7 8 9 10 11 12 db.setProfilingLevel(1, 100) // 记录超过 100ms 的查询 db.system.profile.find().sort ({ts: -1}).limit (10) db.serverStatus().connections db.serverStatus().mem
常见问题 性能问题 1 2 3 4 5 6 7 8 问题: 查询速度慢 解决方案: 1. 检查是否使用索引 db.collection.find().explain() 2. 创建合适的索引 3. 优化查询条件 4. 使用投影减少返回字段
内存溢出 1 2 3 4 5 6 7 8 9 问题: OutOfMemory 解决方案: 1. 增加 WiredTiger 缓存 storage.wiredTiger.engineConfig.cacheSizeGB 2. 优化查询, 减少返回数据量 3. 使用分页 4. 限制聚合管道的内存使用 db.collection.aggregate([...], {allowDiskUse: true})
连接超时 1 2 3 4 5 6 7 问题: Connection timeout 解决方案: 1. 检查网络连接 2. 增加连接超时时间 3. 使用连接池 4. 检查防火墙设置
报错处理 💗💗 MongoDB 报错: E11000 duplicate key error 1 2 3 4 5 6 7 8 9 10 11 错误信息: E11000 duplicate key error collection: mydb.users index: email_1 dup key: { email: "test@example.com" } 错误原因: 违反唯一索引约束 解决方案: 1. 检查数据是否已存在 2. 删除或修改重复数据 3. 如果不需要唯一性, 删除唯一索引 db.users.dropIndex("email_1")
💗💗 MongoDB 报错: WriteConcernError 1 2 3 4 5 6 7 8 9 10 11 12 错误信息: WriteConcernError: waiting for replication timed out 错误原因: 副本集写入确认超时 解决方案: 1. 检查副本集状态 rs.status() 2. 调整 WriteConcern db.collection.insertOne(doc, {writeConcern: {w: 1}}) 3. 检查网络连接
💗💗 MongoDB 报错: CursorNotFound 1 2 3 4 5 6 7 8 9 10 11 错误信息: CursorNotFound: cursor id xxx not found 错误原因: 游标超时或已被关闭 解决方案: 1. 增加游标超时时间 db.collection.find().noCursorTimeout() 2. 使用批量处理 3. 避免长时间持有游标
学习资源
视频
MongoDB 基础入门到高级进阶: https://www.bilibili.com/video/BV1bJ411x7mq
官方文档
MongoDB 官方文档: https://www.mongodb.com/docs/
MongoDB GitHub: https://github.com/mongodb/mongo
书籍
《 MongoDB 权威指南》 : Kristina Chodorow 著
《 MongoDB 实战》 : Kyle Banker 著
教程
MongoDB 入门教程: https://www.runoob.com/mongodb/mongodb-tutorial.html
MongoDB University: https://university.mongodb.com/
工具
MongoDB Compass: 官方可视化工具
Robo 3T: 免费管理工具
Studio 3T: 专业管理工具
社区
MongoDB 中文社区: https://mongoing.com/
Stack Overflow MongoDB 标签: https://stackoverflow.com/questions/tagged/mongodb