阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 最佳实践与常见问题排查

最佳实践与常见问题排查

作者:陈川 阅读数:3114人阅读 分类: MongoDB

最佳实践

索引优化

在MongoDB中,合理的索引设计能显著提升查询性能。复合索引的顺序遵循ESR原则(Equality, Sort, Range):

  • 等值查询字段放最前面
  • 排序字段放在中间
  • 范围查询字段放最后面
// 好的索引示例
db.orders.createIndex({ 
  userId: 1,       // Equality
  createDate: -1,  // Sort
  amount: 1        // Range
})

避免过度索引,每个额外索引都会增加写入时的开销。使用explain()分析查询执行计划:

db.orders.find({
  userId: "user123",
  createDate: { $gt: ISODate("2023-01-01") }
}).sort({ amount: 1 }).explain("executionStats")

文档设计

根据查询模式选择嵌入或引用:

  • 一对少关系且频繁一起查询的数据适合嵌入
  • 一对多关系或独立访问的数据适合引用
// 嵌入文档示例(适合博客文章和评论)
{
  _id: "post1",
  title: "MongoDB指南",
  comments: [
    { user: "Alice", text: "好文章", date: ISODate(...) },
    { user: "Bob", text: "很有帮助", date: ISODate(...) }
  ]
}

// 引用示例(适合用户和订单)
// users集合
{ _id: "user1", name: "张三" }

// orders集合
{ _id: "order1", userId: "user1", items: [...] }

写入优化

批量操作使用bulkWrite()替代单条写入:

const bulkOps = [
  { insertOne: { document: { name: "产品A", price: 100 } } },
  { updateOne: { 
    filter: { name: "产品B" },
    update: { $set: { price: 200 } }
  }},
  { deleteOne: { filter: { name: "产品C" } }}
];

db.products.bulkWrite(bulkOps);

设置合理的写关注级别(write concern),平衡数据安全性和性能:

// 多数节点确认写入
db.products.insertOne(
  { name: "新品", stock: 50 },
  { writeConcern: { w: "majority", j: true } }
)

常见问题排查

性能问题

慢查询通常由以下原因引起:

  1. 缺失索引或索引不当
  2. 全集合扫描
  3. 内存排序(无法使用索引的排序)

使用db.currentOp()查看正在执行的操作:

// 查看执行时间超过3秒的操作
db.currentOp({
  "active": true,
  "secs_running": { "$gt": 3 }
})

通过mongotopmongostat监控数据库活动:

mongotop --host localhost:27017
mongostat --host localhost:27017 --rowcount 5

连接问题

连接池耗尽表现为应用无法获取新连接。调整连接池大小:

// Node.js驱动示例
const client = new MongoClient(uri, {
  poolSize: 50,            // 连接池大小
  connectTimeoutMS: 30000, // 连接超时
  socketTimeoutMS: 60000   // 套接字超时
});

检查连接状态:

db.serverStatus().connections
// 输出示例
{
  "current" : 25,
  "available" : 475,
  "totalCreated" : 42
}

内存使用

MongoDB倾向于使用所有可用内存作为缓存。监控内存使用:

db.serverStatus().mem
// 典型输出
{
  "bits" : 64,
  "resident" : 1024,  // 常驻内存(MB)
  "virtual" : 2048,   // 虚拟内存(MB)
  "supported" : true
}

当出现内存压力时:

  1. 检查工作集是否超过物理内存
  2. 添加更多RAM
  3. 优化查询减少内存使用

复制集问题

常见复制延迟原因:

  • 网络带宽不足
  • 从节点配置较低
  • 主节点写入负载过高

检查复制状态:

rs.printReplicationInfo()
rs.printSecondaryReplicationInfo()

// 详细状态
rs.status()

处理复制延迟:

  1. 增加oplog大小
  2. 提升从节点配置
  3. 限制主节点写入速率

分片集群问题

分片集群常见问题包括:

  • 数据分布不均
  • 查询未包含分片键
  • 配置服务器负载高

检查数据分布:

db.collection.getShardDistribution()

均衡器状态检查:

sh.isBalancerRunning()
sh.getBalancerState()

分片键选择原则:

  1. 基数高(大量不同值)
  2. 写分布均匀
  3. 匹配查询模式

锁竞争

MongoDB使用多粒度锁。查看锁状态:

db.currentOp({ "waitingForLock": true })
db.serverStatus().locks

减少锁竞争的方法:

  1. 缩短事务持续时间
  2. 避免全集合扫描
  3. 使用适当的写关注级别

事务处理

多文档事务注意事项:

  • 事务最大运行时间60秒
  • 事务中操作总数限制
  • 避免在事务中创建集合

事务示例:

const session = client.startSession();
try {
  session.startTransaction({
    readConcern: { level: "snapshot" },
    writeConcern: { w: "majority" }
  });

  await accounts.updateOne(
    { _id: "A" }, { $inc: { balance: -100 } },
    { session }
  );
  
  await accounts.updateOne(
    { _id: "B" }, { $inc: { balance: 100 } },
    { session }
  );

  await session.commitTransaction();
} catch (error) {
  await session.abortTransaction();
  throw error;
} finally {
  session.endSession();
}

存储引擎问题

WiredTiger引擎调优:

// 查看存储引擎状态
db.serverStatus().wiredTiger

// 调整缓存大小(在配置文件中)
storage:
  wiredTiger:
    engineConfig:
      cacheSizeGB: 2

压缩配置:

storage:
  wiredTiger:
    collectionConfig:
      blockCompressor: zlib
    indexConfig:
      prefixCompression: true

查询优化器

MongoDB查询优化器可能选择非最优执行计划。强制使用特定索引:

db.orders.find({ userId: "123", status: "A" })
  .hint({ userId: 1, status: 1 })

重建索引:

db.orders.reIndex()

日期处理

日期查询常见问题包括时区处理和格式转换:

// 查询特定日期的文档(UTC时间)
db.events.find({
  date: {
    $gte: ISODate("2023-01-01T00:00:00Z"),
    $lt: ISODate("2023-01-02T00:00:00Z")
  }
})

// 使用聚合转换时区
db.events.aggregate([
  {
    $project: {
      localDate: {
        $dateToString: {
          format: "%Y-%m-%d %H:%M",
          date: "$date",
          timezone: "+08:00"
        }
      }
    }
  }
])

数组操作

数组查询和更新常见问题:

// 查询包含特定元素的数组
db.products.find({ tags: "electronics" })

// 更新数组中的特定元素
db.products.updateOne(
  { _id: 1, "variants.id": "v1" },
  { $set: { "variants.$.price": 19.99 } }
)

// 使用$elemMatch查询多个条件
db.survey.find({
  results: {
    $elemMatch: { 
      product: "xyz", 
      score: { $gte: 8 } 
    }
  }
})

安全配置

常见安全配置问题:

  1. 未启用认证
  2. 使用弱密码
  3. 网络暴露

基本安全配置:

security:
  authorization: enabled
  keyFile: /path/to/keyfile

net:
  bindIp: 127.0.0.1,192.168.1.100
  port: 27017

创建用户:

db.createUser({
  user: "admin",
  pwd: passwordPrompt(), // 或明文密码
  roles: [ 
    { role: "userAdminAnyDatabase", db: "admin" },
    { role: "readWriteAnyDatabase", db: "admin" }
  ]
})

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

上一篇:事务在应用中的使用

下一篇:引用规范

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌