MongoDB入门教程 - 从零开始学习NoSQL数据库MongoDB
MongoDB入门教程 - 从零开始学习NoSQL数据库MongoDB
目录
- MongoDB简介
- MongoDB安装与配置
- MongoDB基本概念
- MongoDB Shell基础操作
- CRUD操作详解
- 查询操作
- 索引
- 聚合操作
- 数据建模
- MongoDB与Java集成
- MongoDB最佳实践
- 性能优化
- 总结与进阶
1. MongoDB简介
MongoDB是一个开源的、面向文档的NoSQL数据库,由C++语言编写。MongoDB将数据存储为类似JSON的文档,使得数据存储和查询更加灵活。
MongoDB的特点:
- ✅ 文档数据库:以BSON(Binary JSON)格式存储数据
- ✅ 灵活的数据模型:无需预定义表结构
- ✅ 高性能:支持水平扩展,读写性能优秀
- ✅ 丰富的查询语言:支持复杂的查询操作
- ✅ 自动分片:支持大规模数据存储
- ✅ 高可用性:支持副本集,保证数据安全
| 特性 | MongoDB | 关系型数据库(MySQL) |
|---|---|---|
| 数据模型 | 文档模型 | 表模型 |
| 模式 | 动态模式 | 固定模式 |
| 扩展性 | 水平扩展 | 垂直扩展为主 |
| 事务 | 4.0+支持多文档事务 | 完全支持 |
| 查询语言 | MongoDB查询语言 | SQL |
| JOIN操作 | 不支持(需应用层实现) | 支持 |
| 适用场景 | 大数据、内容管理、实时分析 | 事务性应用、复杂关系 |
适合使用MongoDB的场景:
- 📱 内容管理系统:博客、CMS等
- 📊 大数据存储:日志、事件数据
- 🛒 电商平台:商品目录、用户数据
- 📱 移动应用:用户信息、地理位置数据
- 📈 实时分析:实时数据统计
- 🎮 游戏开发:玩家数据、游戏状态
不适合使用MongoDB的场景:
- ❌ 需要复杂JOIN操作的应用
- ❌ 强事务要求的金融系统
- ❌ 数据关系复杂的系统
1.4 MongoDB核心概念
数据库(Database)
- 类似于关系型数据库中的数据库
- 一个MongoDB实例可以包含多个数据库
集合(Collection)
- 类似于关系型数据库中的表
- 一个数据库可以包含多个集合
文档(Document)
- 类似于关系型数据库中的行
- 一个集合可以包含多个文档
- 文档是BSON格式的键值对
2. MongoDB安装与配置
步骤1:下载MongoDB
- 访问MongoDB官网:https://www.mongodb.com/try/download/community
- 选择Windows版本,下载MSI安装包
步骤2:安装MongoDB
- 运行下载的MSI安装包
- 选择"Complete"完整安装
- 选择"Install MongoDB as a Service"
- 设置服务名称和日志路径
- 完成安装
步骤3:验证安装
打开命令提示符,输入:
mongod --version
mongo --version使用Homebrew安装
# 更新Homebrew
brew update
# 安装MongoDB
brew tap mongodb/brew
brew install mongodb-community
# 启动MongoDB服务
brew services start mongodb-community
# 停止MongoDB服务
brew services stop mongodb-community验证安装
mongod --version
mongo --versionUbuntu/Debian
# 导入MongoDB公钥
wget -qO - https://www.mongodb.org/static/pgp/server-6.0.asc | sudo apt-key add -
# 添加MongoDB仓库
echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/6.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-6.0.list
# 更新包列表
sudo apt-get update
# 安装MongoDB
sudo apt-get install -y mongodb-org
# 启动MongoDB服务
sudo systemctl start mongod
sudo systemctl enable mongodCentOS/RHEL
# 创建MongoDB仓库文件
sudo vi /etc/yum.repos.d/mongodb-org-6.0.repo
# 添加以下内容
[mongodb-org-6.0]
name=MongoDB Repository
baseurl=https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/6.0/x86_64/
gpgcheck=1
enabled=1
gpgkey=https://www.mongodb.org/static/pgp/server-6.0.asc
# 安装MongoDB
sudo yum install -y mongodb-org
# 启动MongoDB服务
sudo systemctl start mongod
sudo systemctl enable mongod# 拉取MongoDB镜像
docker pull mongo:latest
# 运行MongoDB容器
docker run -d \
--name mongodb \
-p 27017:27017 \
-v mongodb_data:/data/db \
mongo:latest
# 进入MongoDB Shell
docker exec -it mongodb mongoshMongoDB配置文件通常位于:/etc/mongod.conf(Linux)或安装目录下。
常用配置项:
storage:
dbPath: /var/lib/mongodb # 数据目录
journal:
enabled: true
systemLog:
destination: file
logPath: /var/log/mongodb/mongod.log
logAppend: true
net:
port: 27017 # 端口号
bindIp: 127.0.0.1 # 绑定IP
processManagement:
fork: true # 后台运行2.6 启动和停止MongoDB
启动MongoDB
# Linux
sudo systemctl start mongod
# macOS
brew services start mongodb-community
# Windows(服务会自动启动)停止MongoDB
# Linux
sudo systemctl stop mongod
# macOS
brew services stop mongodb-community
# Windows
# 在服务管理器中停止MongoDB服务重启MongoDB
# Linux
sudo systemctl restart mongod3. MongoDB基本概念
MongoDB中的数据库类似于关系型数据库中的数据库概念。
创建和切换数据库:
// 切换到数据库(如果不存在则创建)
use mydb
// 查看当前数据库
db
// 查看所有数据库
show dbs
// 删除数据库(需要先切换到该数据库)
use mydb
db.dropDatabase()集合类似于关系型数据库中的表。
集合操作:
// 创建集合(显式创建)
db.createCollection("users")
// 创建集合(隐式创建,插入数据时自动创建)
db.users.insertOne({name: "Alice"})
// 查看所有集合
show collections
// 删除集合
db.users.drop()文档是MongoDB中的基本数据单位,类似于关系型数据库中的行。
文档结构:
{
_id: ObjectId("507f1f77bcf86cd799439011"),
name: "Alice",
age: 25,
email: "alice@example.com",
address: {
city: "Beijing",
country: "China"
},
hobbies: ["reading", "swimming"]
}文档特点:
- 文档是键值对的集合
- 键是字符串类型
- 值可以是各种数据类型
_id字段是唯一标识符,自动生成
MongoDB支持丰富的数据类型:
| 类型 | 说明 | 示例 |
|---|---|---|
| String | 字符串 | "Hello" |
| Integer | 整数 | 42 |
| Double | 浮点数 | 3.14 |
| Boolean | 布尔值 | true |
| Date | 日期 | ISODate("2024-01-15T10:30:00Z") |
| ObjectId | 对象ID | ObjectId("...") |
| Array | 数组 | [1, 2, 3] |
| Object | 嵌套对象 | |
| Null | 空值 | null |
| Binary Data | 二进制数据 | BinData(...) |
| Code | JavaScript代码 | Code("...") |
4. MongoDB Shell基础操作
# 连接本地MongoDB
mongosh
# 连接远程MongoDB
mongosh "mongodb://hostname:27017"
# 带认证的连接
mongosh "mongodb://username:password@hostname:27017/database"// 查看帮助
help
// 查看数据库帮助
db.help()
// 查看集合帮助
db.collection.help()
// 查看当前数据库
db
// 查看所有数据库
show dbs
// 查看所有集合
show collections
// 退出
exit// 切换到数据库
use testdb
// 查看当前数据库
db.getName()
// 查看数据库统计信息
db.stats()
// 删除当前数据库
db.dropDatabase()5. CRUD操作详解
insertOne() - 插入单个文档
// 插入单个文档
db.users.insertOne({
name: "Alice",
age: 25,
email: "alice@example.com"
})
// 插入结果
{
acknowledged: true,
insertedId: ObjectId("507f1f77bcf86cd799439011")
}insertMany() - 插入多个文档
// 插入多个文档
db.users.insertMany([
{
name: "Bob",
age: 30,
email: "bob@example.com"
},
{
name: "Charlie",
age: 35,
email: "charlie@example.com"
}
])
// 插入结果
{
acknowledged: true,
insertedIds: [
ObjectId("507f1f77bcf86cd799439012"),
ObjectId("507f1f77bcf86cd799439013")
]
}insert() - 传统插入方法
// 插入单个文档
db.users.insert({
name: "David",
age: 28,
email: "david@example.com"
})
// 插入多个文档
db.users.insert([
{name: "Eve", age: 22},
{name: "Frank", age: 40}
])find() - 查询所有文档
// 查询所有文档
db.users.find()
// 格式化输出
db.users.find().pretty()
// 查询前5条
db.users.find().limit(5)
// 跳过前10条
db.users.find().skip(10)
// 排序
db.users.find().sort({age: 1}) // 升序
db.users.find().sort({age: -1}) // 降序findOne() - 查询单个文档
// 查询第一个匹配的文档
db.users.findOne({age: 25})
// 查询第一个文档
db.users.findOne()条件查询
// 等于
db.users.find({age: 25})
// 不等于
db.users.find({age: {$ne: 25}})
// 大于
db.users.find({age: {$gt: 25}})
// 大于等于
db.users.find({age: {$gte: 25}})
// 小于
db.users.find({age: {$lt: 30}})
// 小于等于
db.users.find({age: {$lte: 30}})
// 在范围内
db.users.find({age: {$in: [25, 30, 35]}})
// 不在范围内
db.users.find({age: {$nin: [25, 30]}})
// 存在字段
db.users.find({email: {$exists: true}})
// 正则表达式
db.users.find({name: /^A/}) // 以A开头
db.users.find({name: /e$/}) // 以e结尾updateOne() - 更新单个文档
// 更新单个文档
db.users.updateOne(
{name: "Alice"},
{$set: {age: 26}}
)
// 更新结果
{
acknowledged: true,
matchedCount: 1,
modifiedCount: 1
}updateMany() - 更新多个文档
// 更新多个文档
db.users.updateMany(
{age: {$lt: 30}},
{$set: {status: "young"}}
)replaceOne() - 替换文档
// 替换整个文档
db.users.replaceOne(
{name: "Alice"},
{
name: "Alice",
age: 26,
email: "alice.new@example.com"
}
)update() - 传统更新方法
// 更新单个文档
db.users.update(
{name: "Alice"},
{$set: {age: 26}}
)
// 更新多个文档
db.users.update(
{age: {$lt: 30}},
{$set: {status: "young"}},
{multi: true}
)更新操作符
// $set - 设置字段值
db.users.updateOne(
{name: "Alice"},
{$set: {age: 26, city: "Beijing"}}
)
// $unset - 删除字段
db.users.updateOne(
{name: "Alice"},
{$unset: {city: ""}}
)
// $inc - 增加数值
db.users.updateOne(
{name: "Alice"},
{$inc: {age: 1}}
)
// $push - 向数组添加元素
db.users.updateOne(
{name: "Alice"},
{$push: {hobbies: "reading"}}
)
// $addToSet - 向数组添加元素(不重复)
db.users.updateOne(
{name: "Alice"},
{$addToSet: {hobbies: "swimming"}}
)
// $pop - 删除数组元素
db.users.updateOne(
{name: "Alice"},
{$pop: {hobbies: 1}} // 1删除最后一个,-1删除第一个
)
// $pull - 删除数组中匹配的元素
db.users.updateOne(
{name: "Alice"},
{$pull: {hobbies: "reading"}}
)deleteOne() - 删除单个文档
// 删除单个文档
db.users.deleteOne({name: "Alice"})
// 删除结果
{
acknowledged: true,
deletedCount: 1
}deleteMany() - 删除多个文档
// 删除多个文档
db.users.deleteMany({age: {$lt: 25}})
// 删除所有文档
db.users.deleteMany({})remove() - 传统删除方法
// 删除单个文档
db.users.remove({name: "Alice"}, {justOne: true})
// 删除多个文档
db.users.remove({age: {$lt: 25}})6. 查询操作
// 只返回指定字段
db.users.find({}, {name: 1, age: 1})
// 排除指定字段
db.users.find({}, {email: 0})
// 注意:不能同时使用包含和排除(_id除外)// AND - 多个条件
db.users.find({
age: {$gt: 25},
city: "Beijing"
})
// OR
db.users.find({
$or: [
{age: {$lt: 25}},
{age: {$gt: 35}}
]
})
// NOT
db.users.find({
age: {$not: {$gt: 30}}
})
// NOR - 都不满足
db.users.find({
$nor: [
{age: {$lt: 25}},
{city: "Shanghai"}
]
})// 查询数组包含某个值
db.users.find({hobbies: "reading"})
// 查询数组包含所有值
db.users.find({hobbies: {$all: ["reading", "swimming"]}})
// 查询数组大小
db.users.find({hobbies: {$size: 3}})
// 查询数组元素
db.users.find({"hobbies.0": "reading"}) // 第一个元素
// 使用$elemMatch
db.users.find({
scores: {
$elemMatch: {
$gt: 80,
$lt: 90
}
}
})// 精确匹配
db.users.find({
"address.city": "Beijing"
})
// 嵌套对象查询
db.users.find({
"address": {
city: "Beijing",
country: "China"
}
})// 创建文本索引
db.articles.createIndex({title: "text", content: "text"})
// 文本搜索
db.articles.find({$text: {$search: "MongoDB tutorial"}})
// 按相关性排序
db.articles.find(
{$text: {$search: "MongoDB"}},
{score: {$meta: "textScore"}}
).sort({score: {$meta: "textScore"}})7. 索引
索引是提高查询性能的重要工具,类似于书籍的目录。
7.2 创建索引
// 创建单字段索引
db.users.createIndex({name: 1}) // 1表示升序,-1表示降序
// 创建复合索引
db.users.createIndex({name: 1, age: -1})
// 创建唯一索引
db.users.createIndex({email: 1}, {unique: true})
// 创建稀疏索引(只索引存在该字段的文档)
db.users.createIndex({phone: 1}, {sparse: true})
// 创建TTL索引(自动删除过期文档)
db.sessions.createIndex(
{createdAt: 1},
{expireAfterSeconds: 3600}
)// 查看集合的所有索引
db.users.getIndexes()
// 查看索引大小
db.users.totalIndexSize()// 删除指定索引
db.users.dropIndex({name: 1})
// 删除所有索引(除了_id)
db.users.dropIndexes()- 单字段索引:在单个字段上创建
- 复合索引:在多个字段上创建
- 多键索引:在数组字段上创建
- 文本索引:支持文本搜索
- 地理空间索引:支持地理位置查询
- 哈希索引:用于分片
8. 聚合操作
聚合操作是对数据进行处理和转换的操作,类似于SQL中的GROUP BY。
$match - 过滤文档
db.orders.aggregate([
{$match: {status: "completed"}}
])$group - 分组
// 按状态分组统计
db.orders.aggregate([
{
$group: {
_id: "$status",
count: {$sum: 1},
totalAmount: {$sum: "$amount"}
}
}
])$project - 投影
db.orders.aggregate([
{
$project: {
orderId: 1,
total: 1,
date: 1
}
}
])$sort - 排序
db.orders.aggregate([
{$sort: {total: -1}}
])$limit - 限制数量
db.orders.aggregate([
{$sort: {total: -1}},
{$limit: 10}
])$skip - 跳过
db.orders.aggregate([
{$skip: 10},
{$limit: 10}
])$unwind - 展开数组
db.orders.aggregate([
{$unwind: "$items"}
])// 数学操作符
{$sum: "$amount"} // 求和
{$avg: "$amount"} // 平均值
{$min: "$amount"} // 最小值
{$max: "$amount"} // 最大值
// 数组操作符
{$first: "$items"} // 第一个元素
{$last: "$items"} // 最后一个元素
{$push: "$item"} // 添加到数组
{$addToSet: "$item"} // 添加到集合(去重)
// 日期操作符
{$year: "$date"} // 提取年份
{$month: "$date"} // 提取月份
{$dayOfMonth: "$date"} // 提取日期// 统计每个用户的订单总数和总金额
db.orders.aggregate([
{
$group: {
_id: "$userId",
orderCount: {$sum: 1},
totalAmount: {$sum: "$amount"},
avgAmount: {$avg: "$amount"}
}
},
{
$sort: {totalAmount: -1}
},
{
$limit: 10
}
])9. 数据建模
嵌入 vs 引用
- 嵌入:适合一对一或一对少的关系
- 引用:适合一对多或多对多的关系
数据访问模式
- 考虑查询频率
- 考虑数据更新频率
数据大小
- 单个文档不超过16MB
// 适合:用户和用户详情(一对一)
{
_id: ObjectId("..."),
name: "Alice",
profile: {
age: 25,
email: "alice@example.com",
address: {
city: "Beijing",
country: "China"
}
}
}// 适合:用户和订单(一对多)
// users集合
{
_id: ObjectId("user1"),
name: "Alice"
}
// orders集合
{
_id: ObjectId("order1"),
userId: ObjectId("user1"),
amount: 100
}博客系统
// 文章集合
{
_id: ObjectId("..."),
title: "MongoDB教程",
content: "...",
author: {
name: "Alice",
email: "alice@example.com"
},
tags: ["MongoDB", "数据库"],
comments: [
{
user: "Bob",
text: "很好的教程",
date: ISODate("...")
}
],
createdAt: ISODate("...")
}10. MongoDB与Java集成
Maven依赖
<dependencies>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-sync</artifactId>
<version>4.11.1</version>
</dependency>
<!-- Spring Data MongoDB(可选) -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
<version>4.1.1</version>
</dependency>
</dependencies>Gradle依赖
dependencies {
implementation 'org.mongodb:mongodb-driver-sync:4.11.1'
implementation 'org.springframework.data:spring-data-mongodb:4.1.1'
}import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoDatabase;
// 连接MongoDB
MongoClient mongoClient = MongoClients.create("mongodb://localhost:27017");
// 获取数据库
MongoDatabase database = mongoClient.getDatabase("mydb");
// 关闭连接
mongoClient.close();插入文档
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import org.bson.Document;
MongoCollection<Document> collection = database.getCollection("users");
// 插入单个文档
Document user = new Document("name", "Alice")
.append("age", 25)
.append("email", "alice@example.com");
collection.insertOne(user);
// 插入多个文档
List<Document> users = Arrays.asList(
new Document("name", "Bob").append("age", 30),
new Document("name", "Charlie").append("age", 35)
);
collection.insertMany(users);查询文档
// 查询所有文档
FindIterable<Document> documents = collection.find();
for (Document doc : documents) {
System.out.println(doc.toJson());
}
// 条件查询
Document query = new Document("age", new Document("$gt", 25));
FindIterable<Document> results = collection.find(query);
// 查询单个文档
Document user = collection.find(new Document("name", "Alice")).first();更新文档
// 更新单个文档
Document filter = new Document("name", "Alice");
Document update = new Document("$set", new Document("age", 26));
collection.updateOne(filter, update);
// 更新多个文档
Document filter2 = new Document("age", new Document("$lt", 30));
Document update2 = new Document("$set", new Document("status", "young"));
collection.updateMany(filter2, update2);删除文档
// 删除单个文档
collection.deleteOne(new Document("name", "Alice"));
// 删除多个文档
collection.deleteMany(new Document("age", new Document("$lt", 25)));import org.bson.codecs.configuration.CodecRegistry;
import org.bson.codecs.pojo.PojoCodecProvider;
// 配置Codec
CodecRegistry pojoCodecRegistry = CodecRegistries.fromRegistries(
MongoClientSettings.getDefaultCodecRegistry(),
CodecRegistries.fromProviders(
PojoCodecProvider.builder().automatic(true).build()
)
);
// 获取集合(使用POJO)
MongoCollection<User> userCollection = database
.getCollection("users", User.class)
.withCodecRegistry(pojoCodecRegistry);
// 插入
User user = new User("Alice", 25, "alice@example.com");
userCollection.insertOne(user);
// 查询
User found = userCollection.find(new Document("name", "Alice")).first();配置
@Configuration
public class MongoConfig {
@Bean
public MongoClient mongoClient() {
return MongoClients.create("mongodb://localhost:27017");
}
@Bean
public MongoTemplate mongoTemplate() {
return new MongoTemplate(mongoClient(), "mydb");
}
}Repository
public interface UserRepository extends MongoRepository<User, String> {
List<User> findByAgeGreaterThan(int age);
User findByName(String name);
}使用
@Autowired
private UserRepository userRepository;
public void test() {
User user = new User("Alice", 25, "alice@example.com");
userRepository.save(user);
List<User> users = userRepository.findByAgeGreaterThan(20);
}11. MongoDB最佳实践
数据库名:小写,使用下划线分隔
集合名:小写,使用下划线分隔,使用复数形式
字段名:驼峰命名或下划线命名(保持一致)
为常用查询字段创建索引
避免过多索引(影响写入性能)
使用复合索引优化多字段查询
定期分析索引使用情况
避免过大的文档(<16MB)
合理使用嵌入和引用
考虑数据访问模式
避免深度嵌套(建议不超过3层)
使用投影只返回需要的字段
使用索引优化查询
避免全表扫描
使用explain()分析查询计划
// 分析查询计划
db.users.find({age: {$gt: 25}}).explain("executionStats")- 启用认证
- 使用角色控制访问权限
- 定期备份数据
- 监控数据库性能
12. 性能优化
// 使用索引
db.users.createIndex({name: 1, age: 1})
// 使用投影
db.users.find({age: 25}, {name: 1, email: 1})
// 限制返回数量
db.users.find().limit(10)
// 使用hint强制使用索引
db.users.find({name: "Alice"}).hint({name: 1})- 批量插入使用insertMany()
- 使用有序插入提高性能
- 合理使用索引(避免过多索引)
- 考虑写入关注级别
MongoClientSettings settings = MongoClientSettings.builder()
.applyConnectionString(new ConnectionString("mongodb://localhost:27017"))
.applyToConnectionPoolSettings(builder ->
builder.maxSize(100)
.minSize(10)
)
.build();
MongoClient mongoClient = MongoClients.create(settings);// 查看服务器状态
db.serverStatus()
// 查看数据库统计
db.stats()
// 查看集合统计
db.users.stats()
// 查看当前操作
db.currentOp()13. 总结与进阶
通过本教程,你已经掌握了:
- ✅ MongoDB的基本概念和安装
- ✅ CRUD操作
- ✅ 查询和索引
- ✅ 聚合操作
- ✅ 数据建模
- ✅ Java集成
- ✅ 最佳实践
副本集(Replica Set)
- 高可用性配置
- 读写分离
- 故障转移
分片(Sharding)
- 水平扩展
- 分片策略
- 分片键选择
事务
- 多文档事务
- 事务性能优化
性能调优
- 查询优化
- 索引优化
- 硬件配置
MongoDB Atlas
- 云数据库服务
- 自动扩展
- 监控和告警
- 官方文档:https://docs.mongodb.com/
- MongoDB University:免费在线课程
- GitHub:https://github.com/mongodb
- 社区论坛:https://developer.mongodb.com/community/forums/
- 构建一个博客系统
- 实现用户管理系统
- 开发数据分析应用
- 构建实时日志系统
结语
MongoDB是一个功能强大、灵活易用的NoSQL数据库。通过本教程的学习,相信你已经掌握了MongoDB的核心功能和使用方法。
记住:
- 多实践:理论结合实践,多写代码
- 理解原理:理解文档模型和索引原理
- 关注性能:注意查询和索引优化
- 持续学习:关注MongoDB新特性和最佳实践
祝你学习愉快,编程顺利! 🚀
本教程由Java突击队学习社区编写,如有问题欢迎反馈。