MongoDB 数据库设计:NoSQL 数据建模最佳实践
深入探讨 MongoDB 数据库设计原则、数据建模策略、性能优化技巧和最佳实践,帮助开发者构建高效、可扩展的 NoSQL 应用。
2025年9月18日
DocsLib Team
MongoDBNoSQL数据库设计数据建模性能优化
MongoDB 数据库设计:NoSQL 数据建模最佳实践
MongoDB 简介
MongoDB 是一个基于文档的 NoSQL 数据库,它使用 BSON(Binary JSON)格式存储数据。与传统的关系型数据库不同,MongoDB 提供了更灵活的数据模型和水平扩展能力。
MongoDB 的核心特性
- 文档导向:数据以文档形式存储,支持嵌套结构
- 动态模式:无需预定义严格的表结构
- 水平扩展:支持分片和副本集
- 丰富的查询语言:支持复杂的查询和聚合操作
- 索引支持:多种索引类型提升查询性能
- ACID 事务:支持多文档事务
数据建模基础
文档结构设计
// 用户文档示例
{
"_id": ObjectId("507f1f77bcf86cd799439011"),
"username": "john_doe",
"email": "john@example.com",
"profile": {
"firstName": "John",
"lastName": "Doe",
"avatar": "https://example.com/avatar.jpg",
"bio": "Software developer passionate about technology",
"location": {
"city": "San Francisco",
"country": "USA",
"coordinates": {
"lat": 37.7749,
"lng": -122.4194
}
}
},
"preferences": {
"theme": "dark",
"language": "en",
"notifications": {
"email": true,
"push": false,
"sms": false
}
},
"tags": ["developer", "javascript", "mongodb"],
"socialLinks": [
{
"platform": "github",
"url": "https://github.com/johndoe"
},
{
"platform": "linkedin",
"url": "https://linkedin.com/in/johndoe"
}
],
"createdAt": ISODate("2023-01-15T10:30:00Z"),
"updatedAt": ISODate("2024-07-30T14:20:00Z"),
"lastLoginAt": ISODate("2024-07-30T09:15:00Z"),
"isActive": true,
"role": "user"
}
// 博客文章文档示例
{
"_id": ObjectId("507f1f77bcf86cd799439012"),
"title": "Getting Started with MongoDB",
"slug": "getting-started-with-mongodb",
"content": "MongoDB is a powerful NoSQL database...",
"excerpt": "Learn the basics of MongoDB database design",
"author": {
"_id": ObjectId("507f1f77bcf86cd799439011"),
"username": "john_doe",
"displayName": "John Doe"
},
"category": {
"_id": ObjectId("507f1f77bcf86cd799439013"),
"name": "Database",
"slug": "database"
},
"tags": [
{
"_id": ObjectId("507f1f77bcf86cd799439014"),
"name": "MongoDB",
"slug": "mongodb"
},
{
"_id": ObjectId("507f1f77bcf86cd799439015"),
"name": "NoSQL",
"slug": "nosql"
}
],
"status": "published",
"publishedAt": ISODate("2024-07-30T12:00:00Z"),
"createdAt": ISODate("2024-07-29T15:30:00Z"),
"updatedAt": ISODate("2024-07-30T11:45:00Z"),
"metadata": {
"readTime": 8,
"wordCount": 1500,
"views": 245,
"likes": 18,
"shares": 5
},
"seo": {
"metaTitle": "Getting Started with MongoDB - Complete Guide",
"metaDescription": "Learn MongoDB basics, data modeling, and best practices",
"keywords": ["mongodb", "nosql", "database", "tutorial"]
},
"comments": [
{
"_id": ObjectId("507f1f77bcf86cd799439016"),
"author": {
"_id": ObjectId("507f1f77bcf86cd799439017"),
"username": "jane_smith",
"displayName": "Jane Smith"
},
"content": "Great article! Very helpful for beginners.",
"createdAt": ISODate("2024-07-30T13:15:00Z"),
"likes": 3,
"replies": [
{
"_id": ObjectId("507f1f77bcf86cd799439018"),
"author": {
"_id": ObjectId("507f1f77bcf86cd799439011"),
"username": "john_doe",
"displayName": "John Doe"
},
"content": "Thank you! Glad you found it useful.",
"createdAt": ISODate("2024-07-30T14:00:00Z")
}
]
}
]
}
嵌入 vs 引用
嵌入式设计(Embedding)
// 适合一对少量的关系
// 用户和地址信息
{
"_id": ObjectId("..."),
"username": "john_doe",
"email": "john@example.com",
"addresses": [
{
"type": "home",
"street": "123 Main St",
"city": "San Francisco",
"state": "CA",
"zipCode": "94102",
"country": "USA",
"isDefault": true
},
{
"type": "work",
"street": "456 Business Ave",
"city": "San Francisco",
"state": "CA",
"zipCode": "94105",
"country": "USA",
"isDefault": false
}
]
}
// 订单和订单项
{
"_id": ObjectId("..."),
"orderNumber": "ORD-2024-001",
"customerId": ObjectId("..."),
"items": [
{
"productId": ObjectId("..."),
"productName": "Laptop",
"sku": "LAP-001",
"quantity": 1,
"unitPrice": 999.99,
"totalPrice": 999.99
},
{
"productId": ObjectId("..."),
"productName": "Mouse",
"sku": "MOU-001",
"quantity": 2,
"unitPrice": 29.99,
"totalPrice": 59.98
}
],
"totalAmount": 1059.97,
"status": "pending",
"createdAt": ISODate("2024-07-30T10:00:00Z")
}
引用式设计(Referencing)
// 适合一对多或多对多的关系
// 用户集合
{
"_id": ObjectId("507f1f77bcf86cd799439011"),
"username": "john_doe",
"email": "john@example.com",
"profile": {
"firstName": "John",
"lastName": "Doe"
}
}
// 文章集合
{
"_id": ObjectId("507f1f77bcf86cd799439012"),
"title": "MongoDB Best Practices",
"content": "...",
"authorId": ObjectId("507f1f77bcf86cd799439011"),
"categoryId": ObjectId("507f1f77bcf86cd799439013"),
"tagIds": [
ObjectId("507f1f77bcf86cd799439014"),
ObjectId("507f1f77bcf86cd799439015")
],
"createdAt": ISODate("2024-07-30T12:00:00Z")
}
// 分类集合
{
"_id": ObjectId("507f1f77bcf86cd799439013"),
"name": "Database",
"slug": "database",
"description": "Database related articles"
}
// 标签集合
{
"_id": ObjectId("507f1f77bcf86cd799439014"),
"name": "MongoDB",
"slug": "mongodb",
"color": "#4DB33D"
}
混合设计模式
// 电商产品设计 - 混合嵌入和引用
{
"_id": ObjectId("..."),
"name": "MacBook Pro 16-inch",
"sku": "MBP-16-2024",
"description": "Powerful laptop for professionals",
"price": {
"current": 2499.99,
"original": 2699.99,
"currency": "USD"
},
"category": {
"_id": ObjectId("..."),
"name": "Laptops",
"path": "Electronics > Computers > Laptops"
},
"brand": {
"_id": ObjectId("..."),
"name": "Apple",
"logo": "https://example.com/apple-logo.png"
},
"specifications": {
"processor": "M3 Pro chip",
"memory": "18GB unified memory",
"storage": "512GB SSD",
"display": "16.2-inch Liquid Retina XDR",
"weight": "2.1 kg",
"dimensions": {
"width": 35.57,
"height": 24.81,
"depth": 1.68,
"unit": "cm"
}
},
"images": [
{
"url": "https://example.com/mbp-main.jpg",
"alt": "MacBook Pro main view",
"isPrimary": true
},
{
"url": "https://example.com/mbp-side.jpg",
"alt": "MacBook Pro side view",
"isPrimary": false
}
],
"inventory": {
"quantity": 25,
"reserved": 3,
"available": 22,
"lowStockThreshold": 5
},
"ratings": {
"average": 4.7,
"count": 156,
"distribution": {
"5": 98,
"4": 42,
"3": 12,
"2": 3,
"1": 1
}
},
"tags": ["laptop", "apple", "professional", "m3-pro"],
"status": "active",
"createdAt": ISODate("2024-01-15T10:00:00Z"),
"updatedAt": ISODate("2024-07-30T14:30:00Z")
}
// 产品评论单独存储(引用)
{
"_id": ObjectId("..."),
"productId": ObjectId("..."),
"userId": ObjectId("..."),
"rating": 5,
"title": "Excellent laptop for development",
"content": "The M3 Pro chip is incredibly fast...",
"verified": true,
"helpful": {
"yes": 23,
"no": 2
},
"createdAt": ISODate("2024-07-25T16:20:00Z")
}
索引策略
基础索引
// 创建单字段索引
db.users.createIndex({ "email": 1 }) // 升序
db.users.createIndex({ "createdAt": -1 }) // 降序
// 创建复合索引
db.posts.createIndex({ "authorId": 1, "createdAt": -1 })
db.products.createIndex({ "category.name": 1, "price.current": 1 })
// 创建文本索引(全文搜索)
db.posts.createIndex({
"title": "text",
"content": "text",
"tags": "text"
}, {
"weights": {
"title": 10,
"content": 5,
"tags": 1
},
"name": "post_text_index"
})
// 创建地理空间索引
db.locations.createIndex({ "coordinates": "2dsphere" })
// 创建哈希索引(用于分片)
db.users.createIndex({ "_id": "hashed" })
// 创建部分索引(只索引满足条件的文档)
db.users.createIndex(
{ "email": 1 },
{ "partialFilterExpression": { "isActive": true } }
)
// 创建稀疏索引(忽略不包含索引字段的文档)
db.users.createIndex(
{ "phoneNumber": 1 },
{ "sparse": true }
)
// 创建唯一索引
db.users.createIndex(
{ "email": 1 },
{ "unique": true }
)
// 创建 TTL 索引(自动删除过期文档)
db.sessions.createIndex(
{ "expiresAt": 1 },
{ "expireAfterSeconds": 0 }
)
索引优化策略
// 查询性能分析
// 使用 explain() 分析查询计划
db.posts.find({ "authorId": ObjectId("..."), "status": "published" })
.sort({ "createdAt": -1 })
.explain("executionStats")
// 索引使用统计
db.posts.aggregate([
{ $indexStats: {} }
])
// 查找未使用的索引
db.runCommand({ "collStats": "posts", "indexDetails": true })
// 复合索引的字段顺序很重要
// 好的索引设计
db.orders.createIndex({
"customerId": 1, // 等值查询
"status": 1, // 等值查询
"createdAt": -1 // 排序字段
})
// 支持的查询模式
db.orders.find({ "customerId": ObjectId("...") })
db.orders.find({ "customerId": ObjectId("..."), "status": "pending" })
db.orders.find({ "customerId": ObjectId("..."), "status": "pending" })
.sort({ "createdAt": -1 })
// ESR 规则:Equality, Sort, Range
// 等值查询 -> 排序 -> 范围查询
db.events.createIndex({
"userId": 1, // Equality
"createdAt": -1, // Sort
"priority": 1 // Range
})
查询优化
高效查询模式
// 使用投影减少网络传输
db.users.find(
{ "isActive": true },
{ "username": 1, "email": 1, "profile.firstName": 1, "profile.lastName": 1 }
)
// 使用 limit() 限制结果数量
db.posts.find({ "status": "published" })
.sort({ "createdAt": -1 })
.limit(20)
// 使用 skip() 进行分页(注意性能问题)
db.posts.find({ "status": "published" })
.sort({ "createdAt": -1 })
.skip(20)
.limit(20)
// 更好的分页方式:基于游标的分页
// 第一页
db.posts.find({ "status": "published" })
.sort({ "_id": -1 })
.limit(20)
// 后续页面
db.posts.find({
"status": "published",
"_id": { $lt: ObjectId("lastIdFromPreviousPage") }
})
.sort({ "_id": -1 })
.limit(20)
// 使用聚合管道进行复杂查询
db.orders.aggregate([
// 匹配条件
{
$match: {
"createdAt": {
$gte: ISODate("2024-01-01T00:00:00Z"),
$lt: ISODate("2024-08-01T00:00:00Z")
},
"status": { $in: ["completed", "shipped"] }
}
},
// 关联用户信息
{
$lookup: {
"from": "users",
"localField": "customerId",
"foreignField": "_id",
"as": "customer"
}
},
// 展开数组
{
$unwind: "$customer"
},
// 分组统计
{
$group: {
"_id": {
"month": { $month: "$createdAt" },
"year": { $year: "$createdAt" }
},
"totalOrders": { $sum: 1 },
"totalRevenue": { $sum: "$totalAmount" },
"averageOrderValue": { $avg: "$totalAmount" },
"uniqueCustomers": { $addToSet: "$customerId" }
}
},
// 计算唯一客户数
{
$addFields: {
"uniqueCustomerCount": { $size: "$uniqueCustomers" }
}
},
// 排序
{
$sort: { "_id.year": 1, "_id.month": 1 }
},
// 格式化输出
{
$project: {
"_id": 0,
"period": {
$concat: [
{ $toString: "$_id.year" },
"-",
{
$cond: {
"if": { $lt: ["$_id.month", 10] },
"then": { $concat: ["0", { $toString: "$_id.month" }] },
"else": { $toString: "$_id.month" }
}
}
]
},
"totalOrders": 1,
"totalRevenue": { $round: ["$totalRevenue", 2] },
"averageOrderValue": { $round: ["$averageOrderValue", 2] },
"uniqueCustomerCount": 1
}
}
])
查询性能监控
// 启用查询分析器
db.setProfilingLevel(2, { slowms: 100 })
// 查看慢查询
db.system.profile.find().sort({ ts: -1 }).limit(5)
// 分析特定查询
db.posts.find({ "authorId": ObjectId("...") })
.explain("executionStats")
// 查看索引使用情况
db.posts.aggregate([
{ $indexStats: {} },
{ $sort: { "accesses.ops": -1 } }
])
// 监控集合统计信息
db.posts.stats()
// 查看当前操作
db.currentOp()
// 终止长时间运行的操作
db.killOp(operationId)
数据建模模式
1. 属性模式(Attribute Pattern)
// 传统方式:每个属性一个字段
{
"_id": ObjectId("..."),
"name": "iPhone 15 Pro",
"color": "Space Black",
"storage": "256GB",
"screen_size": "6.1 inch",
"weight": "187g",
"battery_life": "23 hours",
"camera_megapixels": "48MP",
"water_resistance": "IP68"
}
// 属性模式:使用数组存储属性
{
"_id": ObjectId("..."),
"name": "iPhone 15 Pro",
"attributes": [
{ "key": "color", "value": "Space Black", "type": "string" },
{ "key": "storage", "value": 256, "type": "number", "unit": "GB" },
{ "key": "screen_size", "value": 6.1, "type": "number", "unit": "inch" },
{ "key": "weight", "value": 187, "type": "number", "unit": "g" },
{ "key": "battery_life", "value": 23, "type": "number", "unit": "hours" },
{ "key": "camera_megapixels", "value": 48, "type": "number", "unit": "MP" },
{ "key": "water_resistance", "value": "IP68", "type": "string" }
]
}
// 创建索引支持属性查询
db.products.createIndex({ "attributes.key": 1, "attributes.value": 1 })
// 查询示例
db.products.find({
"attributes": {
$elemMatch: {
"key": "storage",
"value": { $gte: 128 }
}
}
})
2. 桶模式(Bucket Pattern)
// 时间序列数据的桶模式
// 传统方式:每个数据点一个文档
{
"_id": ObjectId("..."),
"sensorId": "sensor_001",
"timestamp": ISODate("2024-07-30T10:00:00Z"),
"temperature": 23.5,
"humidity": 65.2,
"pressure": 1013.25
}
// 桶模式:按时间段分组
{
"_id": ObjectId("..."),
"sensorId": "sensor_001",
"bucketStart": ISODate("2024-07-30T10:00:00Z"),
"bucketEnd": ISODate("2024-07-30T11:00:00Z"),
"count": 60,
"measurements": [
{
"timestamp": ISODate("2024-07-30T10:00:00Z"),
"temperature": 23.5,
"humidity": 65.2,
"pressure": 1013.25
},
{
"timestamp": ISODate("2024-07-30T10:01:00Z"),
"temperature": 23.6,
"humidity": 65.1,
"pressure": 1013.30
}
// ... 更多测量数据
],
"summary": {
"temperature": {
"min": 23.1,
"max": 24.2,
"avg": 23.7
},
"humidity": {
"min": 64.5,
"max": 66.8,
"avg": 65.4
},
"pressure": {
"min": 1012.8,
"max": 1014.1,
"avg": 1013.5
}
}
}
// 网站访问日志的桶模式
{
"_id": ObjectId("..."),
"date": ISODate("2024-07-30T00:00:00Z"),
"hour": 14,
"visits": [
{
"minute": 0,
"pageViews": 145,
"uniqueVisitors": 98,
"bounceRate": 0.32
},
{
"minute": 1,
"pageViews": 152,
"uniqueVisitors": 103,
"bounceRate": 0.29
}
// ... 60分钟的数据
],
"hourlyTotal": {
"pageViews": 8750,
"uniqueVisitors": 5420,
"averageBounceRate": 0.31
}
}
3. 子集模式(Subset Pattern)
// 电影信息 - 主文档
{
"_id": ObjectId("..."),
"title": "The Matrix",
"year": 1999,
"director": "The Wachowskis",
"genre": ["Action", "Sci-Fi"],
"rating": 8.7,
"poster": "https://example.com/matrix-poster.jpg",
"plot": "A computer programmer is led to fight an underground war...",
"runtime": 136,
"cast": [
{
"actor": "Keanu Reeves",
"character": "Neo",
"order": 1
},
{
"actor": "Laurence Fishburne",
"character": "Morpheus",
"order": 2
}
// 只存储主要演员
],
"recentReviews": [
{
"_id": ObjectId("..."),
"userId": ObjectId("..."),
"username": "moviefan123",
"rating": 9,
"comment": "Groundbreaking film!",
"createdAt": ISODate("2024-07-29T15:30:00Z")
}
// 只存储最近的几条评论
],
"stats": {
"totalReviews": 15420,
"averageRating": 8.7,
"totalCast": 45
}
}
// 完整演员信息 - 单独集合
{
"_id": ObjectId("..."),
"movieId": ObjectId("..."),
"cast": [
{
"actor": "Keanu Reeves",
"character": "Neo",
"order": 1,
"bio": "Canadian actor known for...",
"filmography": [...]
}
// 完整的演员列表和详细信息
]
}
// 完整评论信息 - 单独集合
{
"_id": ObjectId("..."),
"movieId": ObjectId("..."),
"userId": ObjectId("..."),
"rating": 9,
"title": "A masterpiece of cinema",
"content": "This movie changed everything...",
"helpful": 45,
"notHelpful": 3,
"createdAt": ISODate("2024-07-29T15:30:00Z")
}
4. 计算模式(Computed Pattern)
// 用户统计信息
{
"_id": ObjectId("..."),
"userId": ObjectId("..."),
"username": "john_doe",
"stats": {
"postsCount": 156,
"commentsCount": 892,
"likesReceived": 2340,
"followersCount": 1250,
"followingCount": 340,
"lastUpdated": ISODate("2024-07-30T14:30:00Z")
},
"monthlyStats": {
"2024-07": {
"postsCount": 12,
"commentsCount": 67,
"likesReceived": 189
},
"2024-06": {
"postsCount": 15,
"commentsCount": 78,
"likesReceived": 234
}
}
}
// 产品销售统计
{
"_id": ObjectId("..."),
"productId": ObjectId("..."),
"sku": "LAP-001",
"salesStats": {
"totalSold": 1250,
"totalRevenue": 1249875.00,
"averagePrice": 999.90,
"lastSaleDate": ISODate("2024-07-30T13:45:00Z"),
"lastUpdated": ISODate("2024-07-30T14:00:00Z")
},
"dailyStats": {
"2024-07-30": {
"sold": 8,
"revenue": 7999.20
},
"2024-07-29": {
"sold": 12,
"revenue": 11998.80
}
}
}
// 使用聚合管道更新计算字段
db.users.aggregate([
{
$lookup: {
"from": "posts",
"localField": "_id",
"foreignField": "authorId",
"as": "posts"
}
},
{
$lookup: {
"from": "comments",
"localField": "_id",
"foreignField": "authorId",
"as": "comments"
}
},
{
$addFields: {
"stats.postsCount": { $size: "$posts" },
"stats.commentsCount": { $size: "$comments" },
"stats.lastUpdated": new Date()
}
},
{
$merge: {
"into": "users",
"whenMatched": "merge"
}
}
])
事务处理
单文档事务
// MongoDB 中的单文档操作是原子性的
db.accounts.updateOne(
{ "_id": ObjectId("...") },
{
$inc: { "balance": -100 },
$push: {
"transactions": {
"_id": ObjectId(),
"type": "debit",
"amount": 100,
"description": "Transfer to savings",
"timestamp": new Date()
}
}
}
)
多文档事务
// 银行转账示例
const session = db.getMongo().startSession()
try {
session.startTransaction()
const accounts = session.getDatabase("bank").getCollection("accounts")
// 从源账户扣款
const debitResult = accounts.updateOne(
{ "_id": ObjectId("sourceAccountId"), "balance": { $gte: 1000 } },
{
$inc: { "balance": -1000 },
$push: {
"transactions": {
"_id": ObjectId(),
"type": "debit",
"amount": 1000,
"toAccount": ObjectId("targetAccountId"),
"description": "Transfer",
"timestamp": new Date()
}
}
},
{ session }
)
if (debitResult.matchedCount === 0) {
throw new Error("Insufficient funds or account not found")
}
// 向目标账户存款
const creditResult = accounts.updateOne(
{ "_id": ObjectId("targetAccountId") },
{
$inc: { "balance": 1000 },
$push: {
"transactions": {
"_id": ObjectId(),
"type": "credit",
"amount": 1000,
"fromAccount": ObjectId("sourceAccountId"),
"description": "Transfer received",
"timestamp": new Date()
}
}
},
{ session }
)
if (creditResult.matchedCount === 0) {
throw new Error("Target account not found")
}
// 记录转账日志
const transfers = session.getDatabase("bank").getCollection("transfers")
transfers.insertOne({
"_id": ObjectId(),
"fromAccount": ObjectId("sourceAccountId"),
"toAccount": ObjectId("targetAccountId"),
"amount": 1000,
"status": "completed",
"timestamp": new Date()
}, { session })
session.commitTransaction()
console.log("Transfer completed successfully")
} catch (error) {
session.abortTransaction()
console.error("Transfer failed:", error.message)
} finally {
session.endSession()
}
事务最佳实践
// 使用重试逻辑处理事务冲突
function withRetry(operation, maxRetries = 3) {
return new Promise(async (resolve, reject) => {
let retries = 0
while (retries < maxRetries) {
const session = db.getMongo().startSession()
try {
session.startTransaction()
const result = await operation(session)
await session.commitTransaction()
session.endSession()
resolve(result)
return
} catch (error) {
await session.abortTransaction()
session.endSession()
if (error.hasErrorLabel("TransientTransactionError") && retries < maxRetries - 1) {
retries++
console.log(`Transaction failed, retrying... (${retries}/${maxRetries})`)
await new Promise(resolve => setTimeout(resolve, Math.pow(2, retries) * 100))
} else {
reject(error)
return
}
}
}
})
}
// 使用示例
withRetry(async (session) => {
const orders = session.getDatabase("ecommerce").getCollection("orders")
const inventory = session.getDatabase("ecommerce").getCollection("inventory")
// 创建订单
const order = {
"_id": ObjectId(),
"customerId": ObjectId("..."),
"items": [
{
"productId": ObjectId("..."),
"quantity": 2,
"price": 99.99
}
],
"total": 199.98,
"status": "pending",
"createdAt": new Date()
}
await orders.insertOne(order, { session })
// 更新库存
for (const item of order.items) {
const updateResult = await inventory.updateOne(
{
"productId": item.productId,
"quantity": { $gte: item.quantity }
},
{
$inc: { "quantity": -item.quantity },
$push: {
"reservations": {
"orderId": order._id,
"quantity": item.quantity,
"timestamp": new Date()
}
}
},
{ session }
)
if (updateResult.matchedCount === 0) {
throw new Error(`Insufficient inventory for product ${item.productId}`)
}
}
return order._id
})
.then(orderId => {
console.log("Order created successfully:", orderId)
})
.catch(error => {
console.error("Order creation failed:", error.message)
})
性能优化
读取优化
// 使用投影减少数据传输
db.users.find(
{ "isActive": true },
{
"username": 1,
"email": 1,
"profile.firstName": 1,
"profile.lastName": 1,
"profile.avatar": 1
}
)
// 使用 allowDiskUse 处理大数据集排序
db.orders.aggregate([
{ $match: { "createdAt": { $gte: ISODate("2024-01-01") } } },
{ $sort: { "totalAmount": -1 } },
{ $limit: 100 }
], { allowDiskUse: true })
// 使用 hint 强制使用特定索引
db.posts.find({ "authorId": ObjectId("...") })
.hint({ "authorId": 1, "createdAt": -1 })
// 使用 readConcern 控制读取一致性
db.orders.find({ "status": "pending" })
.readConcern("majority")
// 批量读取优化
const userIds = [ObjectId("..."), ObjectId("..."), ObjectId("...")]
db.users.find({ "_id": { $in: userIds } })
写入优化
// 批量插入
const documents = []
for (let i = 0; i < 1000; i++) {
documents.push({
"name": `User ${i}`,
"email": `user${i}@example.com`,
"createdAt": new Date()
})
}
db.users.insertMany(documents, { ordered: false })
// 批量更新
db.users.bulkWrite([
{
updateOne: {
"filter": { "_id": ObjectId("...") },
"update": { $set: { "lastLoginAt": new Date() } }
}
},
{
updateOne: {
"filter": { "_id": ObjectId("...") },
"update": { $inc: { "loginCount": 1 } }
}
}
], { ordered: false })
// 使用 upsert 避免重复检查
db.userStats.updateOne(
{ "userId": ObjectId("...") },
{
$inc: { "pageViews": 1 },
$setOnInsert: {
"userId": ObjectId("..."),
"createdAt": new Date()
}
},
{ upsert: true }
)
// 使用 writeConcern 控制写入确认
db.criticalData.insertOne(
{ "data": "important" },
{ writeConcern: { w: "majority", j: true } }
)
内存和存储优化
// 使用紧凑的数据类型
{
"_id": ObjectId("..."),
"status": 1, // 使用数字而不是字符串
"flags": 0b101010, // 使用位字段
"timestamp": NumberLong(1690718400000), // 使用时间戳而不是 Date
"coordinates": [37.7749, -122.4194] // 使用数组而不是对象
}
// 避免深层嵌套
// 不好的设计
{
"user": {
"profile": {
"personal": {
"address": {
"home": {
"street": "123 Main St"
}
}
}
}
}
}
// 更好的设计
{
"user": {
"homeAddress": "123 Main St"
}
}
// 使用数组索引而不是对象键
// 不好的设计
{
"monthlyData": {
"2024-01": { "sales": 1000 },
"2024-02": { "sales": 1200 },
"2024-03": { "sales": 1100 }
}
}
// 更好的设计
{
"monthlyData": [
{ "month": "2024-01", "sales": 1000 },
{ "month": "2024-02", "sales": 1200 },
{ "month": "2024-03", "sales": 1100 }
]
}
分片和副本集
分片策略
// 启用分片
sh.enableSharding("ecommerce")
// 基于哈希的分片(适合均匀分布)
sh.shardCollection("ecommerce.users", { "_id": "hashed" })
// 基于范围的分片(适合范围查询)
sh.shardCollection("ecommerce.orders", { "customerId": 1, "createdAt": 1 })
// 基于地理位置的分片
sh.shardCollection("ecommerce.stores", { "location": "2dsphere" })
// 复合分片键
sh.shardCollection("ecommerce.products", { "categoryId": 1, "_id": 1 })
// 查看分片状态
sh.status()
// 查看集合分片信息
db.orders.getShardDistribution()
// 平衡分片
sh.enableBalancing("ecommerce.orders")
sh.startBalancer()
副本集配置
// 初始化副本集
rs.initiate({
"_id": "myReplicaSet",
"members": [
{ "_id": 0, "host": "mongodb1.example.com:27017", "priority": 2 },
{ "_id": 1, "host": "mongodb2.example.com:27017", "priority": 1 },
{ "_id": 2, "host": "mongodb3.example.com:27017", "priority": 1 },
{ "_id": 3, "host": "mongodb4.example.com:27017", "arbiterOnly": true }
]
})
// 添加副本集成员
rs.add("mongodb5.example.com:27017")
// 添加仲裁者
rs.addArb("mongodb6.example.com:27017")
// 设置读取偏好
db.orders.find().readPref("secondary")
db.orders.find().readPref("secondaryPreferred")
// 查看副本集状态
rs.status()
// 强制选举
rs.stepDown()
监控和维护
性能监控
// 数据库统计信息
db.stats()
// 集合统计信息
db.users.stats()
// 索引统计信息
db.users.aggregate([{ $indexStats: {} }])
// 当前操作
db.currentOp({
"active": true,
"secs_running": { $gte: 5 }
})
// 服务器状态
db.serverStatus()
// 查看连接信息
db.serverStatus().connections
// 查看内存使用
db.serverStatus().mem
// 查看网络统计
db.serverStatus().network
维护操作
// 重建索引
db.users.reIndex()
// 压缩集合
db.runCommand({ compact: "users" })
// 验证集合
db.users.validate()
// 修复数据库
db.repairDatabase()
// 创建数据库备份
mongodump --db ecommerce --out /backup/
// 恢复数据库
mongorestore --db ecommerce /backup/ecommerce/
// 导出集合
mongoexport --db ecommerce --collection users --out users.json
// 导入集合
mongoimport --db ecommerce --collection users --file users.json
最佳实践总结
1. 数据建模原则
- 根据查询模式设计:优先考虑应用的查询需求
- 嵌入 vs 引用:根据数据关系和访问模式选择
- 避免过度规范化:适度的冗余可以提高性能
- 考虑文档大小:单个文档不应超过 16MB
- 使用合适的数据类型:选择最紧凑的数据类型
2. 索引策略
- 为常用查询创建索引:分析查询模式
- 复合索引字段顺序:遵循 ESR 规则
- 定期监控索引使用:删除未使用的索引
- 避免过多索引:平衡查询性能和写入性能
3. 查询优化
- 使用投影:只获取需要的字段
- 限制结果数量:使用 limit() 和分页
- 使用聚合管道:处理复杂的数据转换
- 监控慢查询:启用查询分析器
4. 应用架构
- 连接池管理:合理配置连接池大小
- 读写分离:使用副本集分担读取负载
- 缓存策略:在应用层实现缓存
- 错误处理:实现重试机制和降级策略
5. 运维监控
- 定期备份:制定备份和恢复策略
- 性能监控:监控关键指标
- 容量规划:预估存储和性能需求
- 安全配置:启用认证和授权
MongoDB 的灵活性使其成为现代应用开发的理想选择,但正确的数据建模和优化策略对于构建高性能、可扩展的应用至关重要。通过遵循这些最佳实践,你可以充分发挥 MongoDB 的优势,构建出色的数据驱动应用。