主题
MongoDB文档数据库
课程目标
- 了解MongoDB的基本概念和特点
- 掌握MongoDB的安装和配置方法
- 熟悉MongoDB的CRUD操作
- 了解MongoDB的查询和索引优化
- 掌握MongoDB的聚合操作
- 了解MongoDB的事务支持
- 掌握MongoDB的高可用性方案
- 学习MongoDB的性能优化技巧
- 掌握MongoDB与Python和Go的集成
1. MongoDB简介
1.1 什么是MongoDB
MongoDB是一个开源的、面向文档的NoSQL数据库,由MongoDB Inc.开发和维护。它使用BSON(Binary JSON)格式存储数据,提供了灵活的数据模型和强大的查询能力。
1.2 MongoDB的特点
- 文档模型:使用类似JSON的BSON格式存储数据,支持嵌套文档和数组
- 灵活的模式:不需要预定义表结构,字段可以动态添加和修改
- 强大的查询语言:支持丰富的查询操作,包括范围查询、正则表达式、聚合等
- 索引支持:支持各种类型的索引,包括单字段索引、复合索引、地理空间索引等
- 高可用性:通过复制集实现数据冗余和自动故障转移
- 水平扩展性:通过分片机制支持大规模数据存储
- 事务支持:从4.0版本开始支持多文档事务
- 丰富的生态系统:提供多种语言的驱动程序和工具
1.3 MongoDB的应用场景
- 内容管理系统:灵活的文档模型适合存储各种类型的内容
- 移动应用后端:支持快速开发和迭代
- 实时分析:强大的聚合功能适合数据分析
- IoT应用:可以处理大量的传感器数据
- 电商平台:支持复杂的产品信息和用户行为数据
- 游戏应用:适合存储游戏状态和用户数据
2. MongoDB安装和配置
2.1 在Linux上安装MongoDB
Ubuntu/Debian系统
bash
# 导入MongoDB GPG密钥
wget -qO - https://www.mongodb.org/static/pgp/server-7.0.asc | sudo apt-key add -
# 添加MongoDB源
echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/7.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-7.0.list
# 更新包列表并安装MongoDB
sudo apt-get update
sudo apt-get install -y mongodb-org
# 启动MongoDB服务
sudo systemctl start mongod
sudo systemctl enable mongodCentOS/RHEL系统
bash
# 创建MongoDB源文件
sudo tee /etc/yum.repos.d/mongodb-org-7.0.repo << 'EOF'
[mongodb-org-7.0]
name=MongoDB Repository
baseurl=https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/7.0/x86_64/
gpgcheck=1
enabled=1
gpgkey=https://www.mongodb.org/static/pgp/server-7.0.asc
EOF
# 安装MongoDB
sudo yum install -y mongodb-org
# 启动MongoDB服务
sudo systemctl start mongod
sudo systemctl enable mongod2.2 基本配置
MongoDB的主要配置文件位于/etc/mongod.conf,可以通过修改此文件来配置MongoDB:
yaml
# mongod.conf
# for documentation of all options, see:
# http://docs.mongodb.org/manual/reference/configuration-options/
# where to write logging data.
systemLog:
destination: file
logAppend: true
path: /var/log/mongodb/mongod.log
# Where and how to store data.
storage:
dbPath: /var/lib/mongo
journal:
enabled: true
# network interfaces
net:
port: 27017
bindIp: 127.0.0.1 # 绑定IP地址,0.0.0.0表示允许所有IP访问
# how the process runs
processManagement:
timeZoneInfo: /usr/share/zoneinfo
# security:
# authorization: enabled # 启用身份验证
# operationProfiling:
# replication:
# sharding:
## Enterprise-Only Options:
# auditLog:
# snmp:2.3 启动和停止MongoDB
bash
# 启动MongoDB
sudo systemctl start mongod
# 停止MongoDB
sudo systemctl stop mongod
# 重启MongoDB
sudo systemctl restart mongod
# 查看MongoDB状态
sudo systemctl status mongod2.4 连接到MongoDB
bash
# 使用mongo shell连接
sudo mongosh
# 连接到指定主机和端口
mongosh --host 127.0.0.1 --port 27017
# 使用认证连接
mongosh --host 127.0.0.1 --port 27017 -u admin -p password --authenticationDatabase admin3. MongoDB基本概念
3.1 数据库(Database)
数据库是集合的容器,一个MongoDB实例可以包含多个数据库。默认的数据库是test。
3.2 集合(Collection)
集合是文档的容器,相当于关系型数据库中的表。集合不需要预定义结构,可以存储不同结构的文档。
3.3 文档(Document)
文档是MongoDB中最基本的数据单位,相当于关系型数据库中的行。文档使用BSON格式存储,类似于JSON格式。
3.4 字段(Field)
字段是文档中的键值对,相当于关系型数据库中的列。
3.5 示例文档
json
{
"_id": ObjectId("60c72b9e9b1b2c3d4e5f6a7b"),
"name": "张三",
"age": 30,
"email": "zhangsan@example.com",
"address": {
"city": "北京",
"district": "朝阳区",
"street": "建国路88号"
},
"hobbies": ["阅读", "旅游", "健身"],
"created_at": ISODate("2023-06-14T08:30:00Z")
}4. MongoDB CRUD操作
4.1 创建文档(Create)
javascript
// 插入单个文档
db.users.insertOne({
name: "李四",
age: 25,
email: "lisi@example.com",
address: {
city: "上海",
district: "浦东新区"
},
hobbies: ["音乐", "电影"]
});
// 插入多个文档
db.users.insertMany([
{
name: "王五",
age: 35,
email: "wangwu@example.com",
address: {
city: "广州",
district: "天河区"
},
hobbies: ["美食", "摄影"]
},
{
name: "赵六",
age: 28,
email: "zhaoliu@example.com",
address: {
city: "深圳",
district: "南山区"
},
hobbies: ["编程", "游戏"]
}
]);4.2 读取文档(Read)
javascript
// 查询所有文档
db.users.find();
// 查询单个文档
db.users.findOne({ name: "李四" });
// 条件查询
db.users.find({ age: { $gt: 25 } }); // 年龄大于25的用户
// 投影查询(只返回指定字段)
db.users.find({ age: { $gt: 25 } }, { name: 1, age: 1, _id: 0 });
// 排序查询
db.users.find().sort({ age: 1 }); // 按年龄升序排序
// 限制查询结果数量
db.users.find().limit(5);
// 跳过指定数量的结果
db.users.find().skip(5);4.3 更新文档(Update)
javascript
// 更新单个文档
db.users.updateOne(
{ name: "李四" },
{
$set: { age: 26, email: "lisi_new@example.com" },
$currentDate: { updated_at: true }
}
);
// 更新多个文档
db.users.updateMany(
{ age: { $lt: 30 } },
{
$set: { status: "young" }
}
);
// 替换整个文档
db.users.replaceOne(
{ name: "王五" },
{
name: "王五",
age: 36,
email: "wangwu_new@example.com",
address: {
city: "广州",
district: "天河区",
street: "天河路385号"
}
}
);4.4 删除文档(Delete)
javascript
// 删除单个文档
db.users.deleteOne({ name: "赵六" });
// 删除多个文档
db.users.deleteMany({ age: { $gt: 40 } });
// 删除集合中的所有文档
db.users.deleteMany({});
// 删除整个集合
db.users.drop();5. MongoDB查询和索引
5.1 查询操作符
MongoDB提供了丰富的查询操作符:
| 操作符 | 描述 | 示例 |
|---|---|---|
$eq | 等于 | { age: { $eq: 30 } } |
$ne | 不等于 | { age: { $ne: 30 } } |
$gt | 大于 | { age: { $gt: 30 } } |
$gte | 大于等于 | { age: { $gte: 30 } } |
$lt | 小于 | { age: { $lt: 30 } } |
$lte | 小于等于 | { age: { $lte: 30 } } |
$in | 在指定数组中 | { age: { $in: [25, 30, 35] } } |
$nin | 不在指定数组中 | { age: { $nin: [25, 30, 35] } } |
$and | 逻辑与 | { $and: [{ age: { $gt: 25 } }, { age: { $lt: 35 } }] } |
$or | 逻辑或 | { $or: [{ age: { $lt: 25 } }, { age: { $gt: 35 } }] } |
$not | 逻辑非 | { age: { $not: { $gt: 30 } } } |
$nor | 逻辑NOR | { $nor: [{ age: { $lt: 25 } }, { age: { $gt: 35 } }] } |
$exists | 字段存在 | { email: { $exists: true } } |
$type | 字段类型 | { age: { $type: "int" } } |
$regex | 正则表达式 | { name: { $regex: /^张/ } } |
5.2 索引类型
MongoDB支持多种类型的索引:
- 单字段索引:基于单个字段创建的索引
- 复合索引:基于多个字段创建的索引
- 多键索引:用于数组字段的索引
- 地理空间索引:用于地理位置数据的索引
- 文本索引:用于文本搜索的索引
- 哈希索引:用于基于哈希值的相等查询
5.3 创建索引
javascript
// 创建单字段索引
db.users.createIndex({ name: 1 }); // 1表示升序,-1表示降序
// 创建复合索引
db.users.createIndex({ name: 1, age: -1 });
// 创建唯一索引
db.users.createIndex({ email: 1 }, { unique: true });
// 创建文本索引
db.articles.createIndex({ content: "text", title: "text" });
// 创建地理空间索引
db.places.createIndex({ location: "2dsphere" });5.4 查看索引
javascript
// 查看集合的所有索引
db.users.getIndexes();
// 查看索引大小
db.users.totalIndexSize();5.5 删除索引
javascript
// 删除指定索引
db.users.dropIndex("name_1");
// 删除所有索引(除了_id索引)
db.users.dropIndexes();5.6 查询计划
javascript
// 查看查询计划
db.users.explain().find({ age: { $gt: 30 } });
// 查看详细的执行统计
db.users.explain("executionStats").find({ age: { $gt: 30 } });6. MongoDB聚合操作
6.1 聚合管道
聚合管道是MongoDB中用于数据处理和分析的强大工具,它由多个阶段组成,每个阶段对数据进行特定的处理。
6.2 常用聚合阶段
| 阶段 | 描述 | 示例 |
|---|---|---|
$match | 过滤文档 | { $match: { age: { $gt: 30 } } } |
$project | 重塑文档结构 | { $project: { name: 1, age: 1, _id: 0 } } |
$group | 分组文档 | { $group: { _id: "$address.city", count: { $sum: 1 } } } |
$sort | 排序文档 | { $sort: { count: -1 } } |
$limit | 限制结果数量 | { $limit: 5 } |
$skip | 跳过文档 | { $skip: 5 } |
$unwind | 展开数组 | { $unwind: "$hobbies" } |
$lookup | 左外连接 | { $lookup: { from: "orders", localField: "_id", foreignField: "user_id", as: "orders" } } |
$addFields | 添加字段 | { $addFields: { fullName: { $concat: ["$firstName", " ", "$lastName"] } } } |
$replaceRoot | 替换根文档 | { $replaceRoot: { newRoot: "$address" } } |
6.3 聚合示例
6.3.1 按城市分组统计用户数量
javascript
db.users.aggregate([
{ $match: { age: { $gt: 25 } } },
{ $group: { _id: "$address.city", count: { $sum: 1 } } },
{ $sort: { count: -1 } },
{ $limit: 10 }
]);6.3.2 计算每个用户的 hobbies 数量
javascript
db.users.aggregate([
{ $project: { name: 1, hobbies_count: { $size: "$hobbies" } } },
{ $sort: { hobbies_count: -1 } }
]);6.3.3 连接用户和订单数据
javascript
db.users.aggregate([
{ $match: { age: { $gt: 30 } } },
{ $lookup: {
from: "orders",
localField: "_id",
foreignField: "user_id",
as: "orders"
}
},
{ $addFields: { order_count: { $size: "$orders" } } },
{ $match: { order_count: { $gt: 0 } } },
{ $sort: { order_count: -1 } }
]);7. MongoDB事务支持
7.1 事务概述
MongoDB从4.0版本开始支持多文档事务,4.2版本开始支持分布式事务(跨分片的事务)。事务允许将多个操作作为一个原子单元执行,要么全部成功,要么全部失败。
7.2 事务使用场景
- 金融交易:确保资金转移的原子性
- 订单处理:确保订单创建和库存更新的一致性
- 用户数据更新:确保相关数据的一致性更新
7.3 事务操作
javascript
// 启动事务
session = db.getMongo().startSession();
try {
session.startTransaction();
// 执行操作
db.users.updateOne({ _id: ObjectId("60c72b9e9b1b2c3d4e5f6a7b") }, { $inc: { balance: -100 } });
db.orders.insertOne({ user_id: ObjectId("60c72b9e9b1b2c3d4e5f6a7b"), amount: 100, status: "pending" });
// 提交事务
session.commitTransaction();
print("Transaction committed successfully.");
} catch (error) {
// 回滚事务
session.abortTransaction();
print("Transaction aborted due to error:", error);
} finally {
session.endSession();
}7.4 事务注意事项
- 性能影响:事务会锁定相关资源,可能影响并发性能
- 操作限制:事务中的操作有一些限制,如不能创建集合、不能修改索引等
- 超时设置:事务有默认的超时时间,超过时间会自动中止
- 内存限制:事务使用的内存有限制,大型事务可能会失败
8. MongoDB高可用性
8.1 复制集(Replica Set)
复制集是MongoDB实现高可用性的主要方式,它由多个MongoDB实例组成,包括一个主节点(Primary)和多个从节点(Secondary)。
8.1.1 复制集的作用
- 数据冗余:多个节点存储相同的数据副本
- 自动故障转移:当主节点故障时,自动选举新的主节点
- 读写分离:读操作可以分发到从节点,提高读取性能
8.1.2 复制集架构
- 主节点(Primary):接收所有写操作,将操作记录到 oplog 中
- 从节点(Secondary):复制主节点的 oplog 并应用到本地,保持数据同步
- 仲裁节点(Arbiter):不存储数据,只参与选举投票
8.1.3 配置复制集
javascript
// 初始化复制集
rs.initiate({
_id: "myReplicaSet",
members: [
{ _id: 0, host: "mongodb1:27017" },
{ _id: 1, host: "mongodb2:27017" },
{ _id: 2, host: "mongodb3:27017", arbiterOnly: true }
]
});
// 查看复制集状态
rs.status();
// 添加节点
rs.add("mongodb4:27017");
// 移除节点
rs.remove("mongodb4:27017");
// 查看复制延迟
rs.printSlaveReplicationInfo();8.2 分片(Sharding)
分片是MongoDB实现水平扩展的方式,它将数据分散存储在多个分片服务器上。
8.2.1 分片架构
- 分片服务器(Shard):存储部分数据的MongoDB实例,通常是复制集
- 配置服务器(Config Server):存储分片集群的元数据和配置信息
- 路由服务器(Mongos):作为客户端的接入点,负责将请求路由到相应的分片
8.2.2 分片键
分片键是用于确定数据分布的字段,选择合适的分片键非常重要。
8.2.3 配置分片集群
javascript
// 启动配置服务器
mongod --configsvr --replSet configReplSet --port 27019 --dbpath /data/configdb
// 初始化配置服务器复制集
rs.initiate({
_id: "configReplSet",
members: [
{ _id: 0, host: "config1:27019" },
{ _id: 1, host: "config2:27019" },
{ _id: 2, host: "config3:27019" }
]
});
// 启动路由服务器
mongos --configdb configReplSet/config1:27019,config2:27019,config3:27019 --port 27017
// 启动分片服务器
mongod --shardsvr --replSet shard1ReplSet --port 27018 --dbpath /data/shard1
mongod --shardsvr --replSet shard2ReplSet --port 27020 --dbpath /data/shard2
// 初始化分片复制集
rs.initiate({
_id: "shard1ReplSet",
members: [
{ _id: 0, host: "shard1:27018" }
]
});
rs.initiate({
_id: "shard2ReplSet",
members: [
{ _id: 0, host: "shard2:27020" }
]
});
// 添加分片到集群
sh.addShard("shard1ReplSet/shard1:27018");
sh.addShard("shard2ReplSet/shard2:27020");
// 启用数据库分片
sh.enableSharding("mydb");
// 集合分片(指定分片键)
sh.shardCollection("mydb.users", { "_id": "hashed" });9. MongoDB性能优化
9.1 查询优化
- 使用索引:为常用查询字段创建索引
- 避免全表扫描:使用索引覆盖查询
- 限制返回字段:使用投影减少数据传输
- 限制结果数量:使用 limit 减少数据处理
- 避免使用 $where:$where 操作符会降低查询性能
- 使用聚合管道:对于复杂查询,使用聚合管道代替多次查询
9.2 写入优化
- 批量写入:使用 insertMany 批量插入文档
- 使用 unordered 写入:允许部分写入失败,提高性能
- 避免创建过多索引:索引会影响写入性能
- 使用 WriteConcern:根据业务需求设置合适的写入确认级别
- 预分配空间:使用
db.adminCommand({ setParameter: 1, internalQueryExecMaxBlockingSortBytes: <value> })预分配空间
9.3 存储优化
- 使用 WiredTiger 存储引擎:WiredTiger 提供更好的压缩和并发性能
- 启用压缩:WiredTiger 支持 Snappy 和 zlib 压缩
- 合理设置集合大小:避免单个集合过大
- 使用 capped collections:对于日志等只追加的数据,使用 capped collections
- 定期归档数据:对于历史数据,进行归档处理
9.4 内存优化
- 合理设置 WiredTiger 缓存大小:默认是 RAM 的 50%
- 使用索引覆盖查询:减少内存使用
- 限制结果集大小:避免一次性加载过多数据到内存
- 使用游标:对于大型结果集,使用游标分批处理
9.5 监控和诊断
- 使用 mongostat:监控 MongoDB 实例的状态
- 使用 mongotop:监控集合级别的操作延迟
- 使用 db.serverStatus():查看服务器状态
- 使用 db.collection.stats():查看集合统计信息
- 使用慢查询日志:识别性能问题
10. MongoDB最佳实践
10.1 数据建模
- 使用嵌入文档:对于一对一和一对多关系,优先使用嵌入文档
- 使用引用:对于多对多关系,使用引用
- 避免深层嵌套:嵌套层级不宜过深,一般不超过3-4层
- 合理使用数组:对于数量有限的数组,使用数组字段
- 考虑查询模式:根据查询模式设计数据模型
10.2 安全性
- 启用身份验证:设置用户名和密码
- 配置访问控制:使用角色-based访问控制
- 加密传输:使用 SSL/TLS 加密网络传输
- 加密存储:使用存储加密保护数据
- 定期备份:制定备份策略
10.3 部署建议
- 使用复制集:至少部署3个节点的复制集
- 使用分片:对于大型数据集,使用分片集群
- 合理设置硬件:根据负载选择合适的硬件
- 监控系统:使用监控工具监控 MongoDB 实例
- 定期维护:定期进行索引重建、数据压缩等维护操作
10.4 应用开发
- 使用连接池:重用数据库连接
- 处理连接错误:实现重连机制
- 使用批量操作:减少网络往返
- 合理设置超时:避免长时间阻塞
- 使用事务:对于需要原子性的操作,使用事务
11. MongoDB与Python集成
11.1 安装PyMongo
bash
pip install pymongo11.2 Python连接MongoDB
python
from pymongo import MongoClient
# 连接到MongoDB
client = MongoClient('mongodb://localhost:27017/')
# 选择数据库
db = client['mydb']
# 选择集合
users_collection = db['users']11.3 Python CRUD操作
python
# 插入文档
user = {
"name": "张三",
"age": 30,
"email": "zhangsan@example.com",
"address": {
"city": "北京",
"district": "朝阳区"
},
"hobbies": ["阅读", "旅游"]
}
result = users_collection.insert_one(user)
print(f"插入的文档ID: {result.inserted_id}")
# 查询文档
# 查询单个文档
user = users_collection.find_one({"name": "张三"})
print(f"查询结果: {user}")
# 查询多个文档
users = users_collection.find({"age": {"$gt": 25}})
for user in users:
print(user)
# 更新文档
result = users_collection.update_one(
{"name": "张三"},
{"$set": {"age": 31, "email": "zhangsan_new@example.com"}}
)
print(f"更新的文档数: {result.modified_count}")
# 删除文档
result = users_collection.delete_one({"name": "张三"})
print(f"删除的文档数: {result.deleted_count}")11.4 Python聚合操作
python
# 聚合操作
pipeline = [
{"$match": {"age": {"$gt": 25}}},
{"$group": {"_id": "$address.city", "count": {"$sum": 1}}},
{"$sort": {"count": -1}},
{"$limit": 5}
]
result = list(users_collection.aggregate(pipeline))
print("聚合结果:")
for item in result:
print(item)11.5 Python事务操作
python
# 事务操作
from pymongo import MongoClient
from pymongo.errors import PyMongoError
client = MongoClient('mongodb://localhost:27017/')
db = client['mydb']
users_collection = db['users']
orders_collection = db['orders']
# 启动会话
session = client.start_session()
try:
# 开始事务
with session.start_transaction():
# 更新用户余额
users_collection.update_one(
{"_id": user_id},
{"$inc": {"balance": -100}},
session=session
)
# 创建订单
order = {
"user_id": user_id,
"amount": 100,
"status": "pending",
"created_at": datetime.now()
}
orders_collection.insert_one(order, session=session)
print("事务执行成功")
except PyMongoError as e:
print(f"事务执行失败: {e}")
finally:
# 结束会话
session.end_session()12. MongoDB与Go集成
12.1 安装MongoDB Go驱动
bash
go get go.mongodb.org/mongo-driver/mongo
go get go.mongodb.org/mongo-driver/bson
go get go.mongodb.org/mongo-driver/mongo/options12.2 Go连接MongoDB
go
package main
import (
"context"
"fmt"
"log"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
func main() {
// 设置客户端选项
clientOptions := options.Client().ApplyURI("mongodb://localhost:27017/")
// 连接到MongoDB
client, err := mongo.Connect(context.Background(), clientOptions)
if err != nil {
log.Fatal(err)
}
// 检查连接
err = client.Ping(context.Background(), nil)
if err != nil {
log.Fatal(err)
}
fmt.Println("成功连接到MongoDB")
// 选择数据库和集合
db := client.Database("mydb")
collection := db.Collection("users")
// 关闭连接
defer client.Disconnect(context.Background())
}12.3 Go CRUD操作
go
package main
import (
"context"
"fmt"
"log"
"time"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
// User 结构体
type User struct {
ID primitive.ObjectID `bson:"_id,omitempty" json:"id"`
Name string `bson:"name" json:"name"`
Age int `bson:"age" json:"age"`
Email string `bson:"email" json:"email"`
Address Address `bson:"address" json:"address"`
Hobbies []string `bson:"hobbies" json:"hobbies"`
CreatedAt time.Time `bson:"created_at" json:"created_at"`
}
// Address 结构体
type Address struct {
City string `bson:"city" json:"city"`
District string `bson:"district" json:"district"`
}
func main() {
// 连接到MongoDB
clientOptions := options.Client().ApplyURI("mongodb://localhost:27017/")
client, err := mongo.Connect(context.Background(), clientOptions)
if err != nil {
log.Fatal(err)
}
defer client.Disconnect(context.Background())
db := client.Database("mydb")
collection := db.Collection("users")
ctx := context.Background()
// 插入文档
user := User{
Name: "李四",
Age: 25,
Email: "lisi@example.com",
Address: Address{
City: "上海",
District: "浦东新区",
},
Hobbies: []string{"音乐", "电影"},
CreatedAt: time.Now(),
}
result, err := collection.InsertOne(ctx, user)
if err != nil {
log.Fatal(err)
}
fmt.Printf("插入的文档ID: %v\n", result.InsertedID)
// 查询文档
// 查询单个文档
var foundUser User
err = collection.FindOne(ctx, bson.M{"name": "李四"}).Decode(&foundUser)
if err != nil {
log.Fatal(err)
}
fmt.Printf("查询结果: %+v\n", foundUser)
// 查询多个文档
cursor, err := collection.Find(ctx, bson.M{"age": bson.M{"$gt": 20}})
if err != nil {
log.Fatal(err)
}
defer cursor.Close(ctx)
var users []User
if err = cursor.All(ctx, &users); err != nil {
log.Fatal(err)
}
fmt.Println("查询多个文档结果:")
for _, u := range users {
fmt.Printf("%+v\n", u)
}
// 更新文档
updateResult, err := collection.UpdateOne(
ctx,
bson.M{"name": "李四"},
bson.M{"$set": bson.M{"age": 26, "email": "lisi_new@example.com"}},
)
if err != nil {
log.Fatal(err)
}
fmt.Printf("更新的文档数: %v\n", updateResult.ModifiedCount)
// 删除文档
deleteResult, err := collection.DeleteOne(ctx, bson.M{"name": "李四"})
if err != nil {
log.Fatal(err)
}
fmt.Printf("删除的文档数: %v\n", deleteResult.DeletedCount)
}12.4 Go聚合操作
go
package main
import (
"context"
"fmt"
"log"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
func main() {
// 连接到MongoDB
clientOptions := options.Client().ApplyURI("mongodb://localhost:27017/")
client, err := mongo.Connect(context.Background(), clientOptions)
if err != nil {
log.Fatal(err)
}
defer client.Disconnect(context.Background())
db := client.Database("mydb")
collection := db.Collection("users")
ctx := context.Background()
// 聚合操作
pipeline := mongo.Pipeline{
bson.D{{"$match", bson.D{{"age", bson.D{{"$gt", 25}}}}}},
bson.D{{"$group", bson.D{{"_id", "$address.city"}, {"count", bson.D{{"$sum", 1}}}}}},
bson.D{{"$sort", bson.D{{"count", -1}}}},
bson.D{{"$limit", 5}},
}
cursor, err := collection.Aggregate(ctx, pipeline)
if err != nil {
log.Fatal(err)
}
defer cursor.Close(ctx)
var results []bson.M
if err = cursor.All(ctx, &results); err != nil {
log.Fatal(err)
}
fmt.Println("聚合结果:")
for _, result := range results {
fmt.Printf("%+v\n", result)
}
}13. 课程总结
13.1 关键知识点
- MongoDB是一个面向文档的NoSQL数据库,使用BSON格式存储数据
- MongoDB的基本概念包括数据库、集合、文档和字段
- MongoDB支持丰富的CRUD操作和查询操作符
- MongoDB提供多种类型的索引,以提高查询性能
- MongoDB的聚合管道功能强大,适合数据分析
- MongoDB支持多文档事务,确保数据一致性
- MongoDB通过复制集实现高可用性,通过分片实现水平扩展性
- MongoDB的性能优化包括查询优化、写入优化、存储优化和内存优化
- MongoDB与Python和Go等语言有良好的集成支持
13.2 学习建议
- 动手实践:安装MongoDB并进行实际操作
- 深入理解:理解MongoDB的数据模型和查询机制
- 掌握索引:学习如何创建和使用索引
- 学习聚合:掌握聚合管道的使用
- 了解高可用:学习复制集和分片的配置
- 实践项目:在实际项目中使用MongoDB
- 持续学习:关注MongoDB的新版本和新特性
13.3 参考资源
通过本课程的学习,你应该已经掌握了MongoDB的基本概念和使用方法,能够在实际项目中应用MongoDB进行数据存储和管理。MongoDB作为一款强大的NoSQL数据库,在现代应用开发中有着广泛的应用,掌握它将为你的技术栈增添重要的一环。