{T}

MongoDB 运维管理

本文档介绍 MongoDB 的备份恢复、监控、性能调优和安全配置。

备份与恢复

mongodump / mongorestore

bash
# 备份整个数据库
mongodump --uri="mongodb://localhost:27017/mydb" --out=/backup/$(date +%Y%m%d)

# 备份指定集合
mongodump --uri="mongodb://localhost:27017/mydb" --collection=users --out=/backup/

# 备份并压缩
mongodump --uri="mongodb://localhost:27017/mydb" --gzip --archive=/backup/mydb.gz

# 备份副本集(从从节点备份)
mongodump --uri="mongodb://secondary:27017/mydb?readPreference=secondary" --out=/backup/

# 恢复数据库
mongorestore --uri="mongodb://localhost:27017/mydb" /backup/20240115/mydb/

# 恢复压缩备份
mongorestore --uri="mongodb://localhost:27017/mydb" --gzip --archive=/backup/mydb.gz

# 恢复时删除现有数据
mongorestore --drop --uri="mongodb://localhost:27017/mydb" /backup/mydb/

# 恢复指定集合
mongorestore --uri="mongodb://localhost:27017/mydb" --collection=users /backup/mydb/users.bson

mongoexport / mongoimport

bash
# 导出为 JSON
mongoexport --uri="mongodb://localhost:27017/mydb" --collection=users --out=users.json

# 导出为 JSON(美化格式)
mongoexport --uri="mongodb://localhost:27017/mydb" --collection=users --pretty --out=users.json

# 导出为 CSV
mongoexport --uri="mongodb://localhost:27017/mydb" --collection=users --type=csv \
  --fields=username,email,age --out=users.csv

# 查询条件导出
mongoexport --uri="mongodb://localhost:27017/mydb" --collection=users \
  --query='{"status": "active"}' --out=active_users.json

# 导入 JSON
mongoimport --uri="mongodb://localhost:27017/mydb" --collection=users --file=users.json

# 导入 JSON(合并模式)
mongoimport --uri="mongodb://localhost:27017/mydb" --collection=users \
  --file=users.json --mode=merge

# 导入 CSV
mongoimport --uri="mongodb://localhost:27017/mydb" --collection=users --type=csv \
  --headerline --file=users.csv

云备份(Atlas)

bash
# MongoDB Atlas 提供自动化云备份
# 功能:
# - 自动快照备份
# - 时间点恢复(PITR)
# - 跨区域备份
# - 按需快照

# 通过 Atlas 控制台或 API 管理
# Atlas CLI 示例:
atlas backups snapshots create --clusterName myCluster --desc "Manual backup"
atlas backups snapshots list --clusterName myCluster

文件系统快照

bash
# 使用 LVM 快照
lvcreate -L 10G -s -n mongodb_snapshot /dev/vg0/mongodb

# 恢复
lvconvert --merge /dev/vg0/mongodb_snapshot

# 注意:快照前应先执行 fsyncLock,完成后解锁

监控

内置命令

javascript
// 服务器状态
db.serverStatus()

// 数据库统计
db.stats()

// 集合统计
db.collection.stats()

// 实时监控
db.collection.watch()

// 当前操作
db.currentOp()

// 查看长时间运行的操作
db.currentOp({ "secs_running": { $gt: 5 } })

// 杀死操作
db.killOp(opId)

// 性能分析
db.setProfilingLevel(1, 50)  // 记录超过 50ms 的操作
db.system.profile.find().sort({ ts: -1 }).limit(10)

// 查看复制状态
rs.printSlaveReplicationInfo()
rs.printReplicationInfo()

关键监控指标

指标说明警告阈值
connections.current当前连接数接近 maxPoolSize
opcounters.*操作计数器异常增长
mem.resident内存使用量接近物理内存
page_faults页面错误持续增长
replication lag复制延迟> 10s
wiredTiger.cache.*缓存使用> 95%
network.bytesIn/Out网络流量接近带宽上限
db.collection.stats().size集合大小持续增长

日志管理

yaml
# mongod.conf
systemLog:
  destination: file
  path: /var/log/mongodb/mongod.log
  logAppend: true
  logRotate: reopen
  verbosity: 1
  component:
    query:
      verbosity: 2
    replication:
      verbosity: 1
    sharding:
      verbosity: 1
bash
# 慢查询日志
# 设置慢查询阈值(毫秒)
db.setProfilingLevel(1, 100)

# 查看慢查询
db.system.profile.find({ millis: { $gt: 100 } }).sort({ ts: -1 })

# 分析慢查询
db.system.profile.find({
  millis: { $gt: 100 },
  ns: "mydb.users"
}).sort({ millis: -1 }).limit(10)

监控工具

bash
# mongostat - 实时统计
mongostat --uri="mongodb://localhost:27017" 5  # 每5秒输出一次

# mongotop - 读写时间统计
mongotop --uri="mongodb://localhost:27017" 10

# MongoDB Compass - 可视化监控
# MongoDB Atlas - 云监控
# Prometheus + Grafana - 自定义监控

性能调优

索引优化

javascript
// 查看索引使用情况
db.collection.aggregate([
  { $indexStats: {} }
])

// 查看未使用的索引
db.collection.aggregate([
  { $indexStats: {} },
  { $match: { "accesses.ops": 0 } }
])

// 强制使用索引
db.collection.find().hint({ field: 1 })

// 禁用索引(测试用)
db.collection.find().hint({ $natural: 1 })

// 分析查询计划
db.collection.find().explain("executionStats")

// 检查索引大小
db.collection.totalIndexSize()

查询优化

优化策略:

  • 使用投影:只返回需要的字段
  • 使用索引覆盖:查询字段都在索引中
  • 避免 $where:使用 $expr 代替
  • 限制结果集:使用 limit()
  • 批量操作:使用 insertManybulkWrite
  • 避免 $or 与索引:改用 $in
javascript
// 不好的查询
db.users.find({
  $or: [
    { name: "Alice" },
    { name: "Bob" }
  ]
})

// 优化后
db.users.find({ name: { $in: ["Alice", "Bob"] } })

// 使用 $expr 替代 $where
// 不好的方式
db.users.find({ $where: "this.age > 18" })

// 优化后
db.users.find({ $expr: { $gt: ["$age", 18] } })

内存优化

yaml
# WiredTiger 缓存配置
storage:
  wiredTiger:
    engineConfig:
      cacheSizeGB: 4  # 建议:物理内存的 50%-60%
javascript
// 查看缓存使用情况
db.serverStatus().wiredTiger.cache

// 检查内存压力
db.serverStatus().mem

连接池优化

javascript
// Node.js 连接池配置
const client = new MongoClient(uri, {
  maxPoolSize: 100,      // 最大连接数
  minPoolSize: 10,       // 最小连接数
  maxIdleTimeMS: 30000,  // 空闲连接超时
  waitQueueTimeoutMS: 5000,  // 获取连接超时
  connectTimeoutMS: 10000,
  socketTimeoutMS: 45000
})

写入优化

javascript
// 批量写入
db.collection.insertMany([...], { ordered: false })

// 使用 bulkWrite
db.collection.bulkWrite([
  { insertOne: { document: {...} } },
  { updateOne: { filter: {...}, update: {...} } }
], { ordered: false })

// 调整写关注
db.collection.insertOne({...}, { writeConcern: { w: 1 } })  // 单节点确认

安全配置

访问控制

javascript
// 创建管理员
use admin
db.createUser({
  user: "admin",
  pwd: "strongPassword123!",
  roles: ["root"]
})

// 创建应用用户
use mydb
db.createUser({
  user: "appUser",
  pwd: "appPassword123!",
  roles: [
    { role: "readWrite", db: "mydb" }
  ]
})

// 创建只读用户
db.createUser({
  user: "readOnly",
  pwd: "readOnly123!",
  roles: [
    { role: "read", db: "mydb" }
  ]
})

// 更新用户密码
db.changeUserPassword("appUser", "newPassword123!")

// 删除用户
db.dropUser("oldUser")

// 查看用户权限
db.getUser("appUser")

网络安全

yaml
# mongod.conf
net:
  port: 27017
  bindIp: 127.0.0.1,10.0.0.1  # 限制绑定 IP
  tls:
    mode: requireTLS
    certificateKeyFile: /path/to/cert.pem
    CAFile: /path/to/ca.pem
    allowConnectionsWithoutCertificates: false

security:
  authorization: enabled
  # 副本集认证
  keyFile: /path/to/keyfile
bash
# 生成 keyFile(副本集认证用)
openssl rand -base64 756 > /path/to/keyfile
chmod 400 /path/to/keyfile

# 防火墙配置(仅允许内网访问)
sudo ufw allow from 10.0.0.0/8 to any port 27017
sudo ufw enable

防止 NoSQL 注入

javascript
// 错误示例 - 直接使用用户输入
const query = { username: req.body.username }  // 危险!

// 正确示例 - 验证和转义
const username = req.body.username
if (typeof username !== 'string') {
  throw new Error('Invalid username')
}
const query = { username: username }

// 使用 Mongoose 验证
const schema = new mongoose.Schema({
  username: { type: String, required: true }
})

// 检查操作符注入
function sanitizeQuery(query) {
  const forbiddenKeys = ['$where', '$function', '$accumulator']
  
  function sanitize(obj) {
    if (typeof obj !== 'object' || obj === null) return obj
    
    for (const key of Object.keys(obj)) {
      if (forbiddenKeys.includes(key)) {
        delete obj[key]
      } else if (typeof obj[key] === 'object') {
        sanitize(obj[key])
      }
    }
    return obj
  }
  
  return sanitize(JSON.parse(JSON.stringify(query)))
}

审计

yaml
# mongod.conf
auditLog:
  destination: file
  format: JSON
  path: /var/log/mongodb/audit.log
  filter: '{ atype: { $in: [ "authenticate", "createUser", "dropUser" ] } }'

维护操作

压缩与修复

javascript
// 压缩集合(需要维护窗口)
db.runCommand({ compact: "users" })

// 修复数据库
db.repairDatabase()  // 需要磁盘空间等于数据库大小

// 查看压缩效果
db.collection.stats()

索引重建

javascript
// 后台重建索引
db.collection.createIndex({ field: 1 }, { background: true })

// 重建所有索引
db.collection.reIndex()

数据迁移

javascript
// 迁移数据到新集合
db.source.aggregate([
  { $out: "destination" }
])

// 批量迁移
db.source.find().forEach(doc => {
  db.destination.insertOne(doc)
})

上一篇架构与高级特性
下一篇常见问题解答