目录

MongoDB 最佳实践

快速入门

安装与版本

当前使用版本为 MongoDB 8.2.6

macOS

# 使用 Homebrew 安装
brew tap mongodb/brew
brew install [email protected]

# 启动服务
brew services start [email protected]

# 或手动启动
mongod --config /usr/local/etc/mongod.conf

# 连接
mongosh

Windows

  1. 下载 MongoDB Community Server Windows 版本
  2. 运行安装程序
  3. MongoDB 默认安装在 C:\Program Files\MongoDB\Server\8.0\bin
  4. 创建数据目录:
mkdir C:\data\db
mongod --dbpath C:\data\db

Linux (Ubuntu)

# 导入 MongoDB GPG 密钥
curl -fsSL https://www.mongodb.org/static/pgp/server-8.2.asc | sudo gpg -o /usr/share/keyrings/mongodb-server-8.2.gpg --dearmor

# 添加 MongoDB 仓库
echo "deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-8.2.gpg ] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/8.2 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-8.2.list

# 更新并安装
sudo apt update
sudo apt install -y mongodb-org

# 启动服务
sudo systemctl start mongod
sudo systemctl enable mongod

# 连接
mongosh

Docker Compose 部署

services:
  mongodb:
    image: mongo:8.2.6
    container_name: mongodb
    restart: unless-stopped
    ports:
      - "27017:27017"
    environment:
      MONGO_INITDB_ROOT_USERNAME: admin
      MONGO_INITDB_ROOT_PASSWORD: password123
    volumes:
      - mongodb_data:/data/db
    healthcheck:
      test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
      interval: 10s
      timeout: 5s
      retries: 5

volumes:
  mongodb_data:

启动:docker compose up -d

连接:mongosh mongodb://admin:password123@localhost:27017

基本操作

// 查看数据库
show dbs

// 使用数据库
use myapp

// 查看集合
show collections

// 基本 CRUD
db.users.insertOne({ name: "Alice", email: "[email protected]" })
db.users.find({ name: "Alice" })
db.users.updateOne({ _id: 1 }, { $set: { age: 30 } })
db.users.deleteOne({ _id: 1 })

Schema 设计

原则

  1. 嵌入式文档 vs 引用
  2. 考虑查询模式设计
  3. 避免 unbounded 数组

嵌入式文档

适用于 1:1 或 1:few 关系:

// 好:嵌入式
{
  _id: ObjectId("..."),
  name: "Alice",
  profile: {
    bio: "Engineer",
    location: "Beijing",
    website: "https://alice.dev"
  }
}

// 好:小型数组
{
  name: "Order",
  items: [
    { product: "Widget", quantity: 2 },
    { product: "Gadget", quantity: 1 }
  ]
}

引用文档

适用于 1:many 或 many:many 关系:

// 用户集合
{
  _id: ObjectId("..."),
  name: "Alice",
  email: "[email protected]"
}

// 订单集合(引用用户)
{
  _id: ObjectId("..."),
  user_id: ObjectId("..."),  // 引用
  items: [...],
  total: 99.99
}

Schema 验证

db.createCollection("users", {
  validator: {
    $jsonSchema: {
      bsonType: "object",
      required: ["name", "email"],
      properties: {
        name: {
          bsonType: "string",
          description: "must be a string"
        },
        email: {
          bsonType: "string",
          pattern: "@.+",
          description: "must be a valid email"
        },
        age: {
          bsonType: "int",
          minimum: 0,
          maximum: 150
        }
      }
    }
  }
})

索引策略

创建索引

// 单字段索引
db.users.createIndex({ email: 1 }, { unique: true })

// 复合索引
db.orders.createIndex({ user_id: 1, created_at: -1 })

// 多键索引(数组字段)
db.products.createIndex({ tags: 1 })

// 文本索引
db.articles.createIndex({ title: "text", content: "text" })

// 地理空间索引
db.locations.createIndex({ coordinates: "2dsphere" })

索引原则

原则 说明
选择性 基数高的字段
查询模式 匹配查询条件的字段顺序
避免过多样本 避免在小集合上建索引
考虑写开销 索引影响写入性能

索引类型

// 唯一索引
db.users.createIndex({ email: 1 }, { unique: true })

// 部分索引
db.logs.createIndex(
  { timestamp: 1 },
  { partialFilterExpression: { level: "error" } }
)

// 稀疏索引
db.sessions.createIndex(
  { token: 1 },
  { sparse: true }
)

// TTL 索引(自动删除)
db.logs.createIndex(
  { created_at: 1 },
  { expireAfterSeconds: 60 * 60 * 24 * 7 }  // 7 天
)

查看和删除索引

// 查看索引
db.users.getIndexes()

// 删除索引
db.users.dropIndex("email_1")

// 删除所有非 _id 索引
db.users.dropIndexes()

聚合管道

基本结构

db.orders.aggregate([
  { $match: { status: "completed" } },
  { $group: { _id: "$user_id", total: { $sum: "$amount" } } },
  { $sort: { total: -1 } },
  { $limit: 10 }
])

常用阶段

// $match - 过滤
{ $match: { status: "active", age: { $gte: 18 } } }

// $group - 分组
{ $group: {
    _id: "$category",
    count: { $sum: 1 },
    avgPrice: { $avg: "$price" }
  }
}

// $project - 选择字段
{ $project: { name: 1, email: 1, _id: 0 } }

// $sort - 排序
{ $sort: { created_at: -1 } }

// $limit - 限制数量
{ $limit: 100 }

// $skip - 跳过
{ $skip: 50 }

// $lookup - JOIN
{ $lookup: {
    from: "users",
    localField: "user_id",
    foreignField: "_id",
    as: "user"
  }
}

常见模式

// 用户订单统计
db.orders.aggregate([
  { $match: { user_id: ObjectId("...") } },
  { $group: {
      _id: "$user_id",
      orderCount: { $sum: 1 },
      totalSpent: { $sum: "$amount" },
      avgOrderValue: { $avg: "$amount" }
    }
  }
])

// 日期分组
{ $group: {
    _id: {
      year: { $year: "$created_at" },
      month: { $month: "$created_at" }
    },
    count: { $sum: 1 }
  }
}

性能优化

Explain

// 分析查询
db.orders.find({ user_id: ObjectId("...") }).explain("executionStats")

// 关键指标
// - executionTimeMillis: 执行时间
// - totalDocsExamined: 检查的文档数
// - nReturned: 返回的文档数

覆盖查询

// 好:索引覆盖查询
db.users.createIndex({ email: 1, name: 1 })
db.users.find({ email: "[email protected]" }, { name: 1, _id: 0 })

// 查询计划使用索引
// executionStats.nReturned === totalDocsExamined

避免全集合扫描

// 不好:全集合扫描
db.users.find({ name: /.*alice.*/i })

// 好:使用索引
db.users.find({ email: "[email protected]" })

// 使用正则前缀
db.users.find({ name: /^alice/ })  // 可以使用索引

副本集

复制原理

  • Primary: 接受写操作
  • Secondary: 复制 Primary 的 oplog
  • 投票节点: 参与选举

配置副本集

// 初始化副本集
rs.initiate({
  _id: "myapp",
  members: [
    { _id: 0, host: "localhost:27017" },
    { _id: 1, host: "localhost:27018" },
    { _id: 2, host: "localhost:27019", arbiterOnly: true }
  ]
})

// 状态检查
rs.status()

// 查看主节点
db.hello()

分片

分片策略

// 哈希分片(均匀分布)
sh.shardCollection("myapp.orders", { _id: "hashed" })

// 范围分片(按范围分布)
sh.shardCollection("myapp.users", { user_id: 1 })

分片注意事项

  • 选择好的 shard key(高基数、避免单调递增)
  • 使用 mongos 路由
  • 考虑 zone sharding 按地区分布

安全

认证

// 创建用户管理员
use admin
db.createUser({
  user: "admin",
  pwd: "secure_password",
  roles: [
    { role: "userAdminAnyDatabase", db: "admin" },
    { role: "readWriteAnyDatabase", db: "admin" }
  ]
})

// 连接时认证
mongosh -u admin -p --authenticationDatabase admin

授权

// 为应用创建用户
use myapp
db.createUser({
  user: "myapp_user",
  pwd: "app_password",
  roles: [
    { role: "readWrite", db: "myapp" }
  ]
})

相关资源