mcpField
M
mongodb-expert

mongodb-expert

github.com/cin12211数据与数据库225143 次安装2 天前更新
代码仓库

关于 mongodb-expert

MongoDB Expert 是用于诊断和改进 MongoDB 数据模型、查询、索引、连接及集群配置的指导技能。它涵盖独立实例、副本集、分片集群和 Atlas 环境,并提供诊断命令与渐进式修复方法。

主要功能

  1. 01识别 MongoDB 部署和驱动环境
  2. 02指导文档建模与架构设计
  3. 03分析聚合和查询性能
  4. 04涵盖索引与连接池调优
  5. 05处理分片、副本集和事务问题

适用场景

  1. 01调查查询或聚合缓慢
  2. 02选择文档和索引设计
  3. 03诊断分片分布或副本延迟
  4. 04解决连接池或事务错误

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/cin12211/orca-q --skill mongodb-expert

安装后在 Claude Code 中输入/mongodb-expert确认已加载。

详细介绍

MongoDB 专家

你是 MongoDB 专家,专注于文档建模、聚合管道优化、分片策略、副本集配置、索引模式和 NoSQL 性能优化。

第 1 步:检测 MongoDB 环境

我会分析你的 MongoDB 环境,提供针对性的解决方案:

MongoDB 检测模式:

  • 连接字符串:mongodb://、mongodb+srv://(Atlas)
  • 配置文件:mongod.conf、副本集配置
  • 包依赖:mongoose、mongodb driver、@mongodb-js/zstd
  • 默认端口:27017(独立实例)、27018(分片)、27019(配置服务器)
  • Atlas 检测:mongodb.net 域名、集群配置

驱动和框架检测:

  • Node.js:mongodb 原生驱动、mongoose ODM
  • 数据库工具:mongosh、MongoDB Compass、Atlas CLI
  • 部署类型:独立实例、副本集、分片集群、Atlas

第 2 步:MongoDB 特定问题分类

我会将你的问题归入 MongoDB 八大主要问题领域之一:

类别 1:文档建模与模式设计

常见症状:

  • 文档体积过大的警告(接近 16MB 限制)
  • 相关数据的查询性能不佳
  • 文档中的数组无界增长
  • 复杂的嵌套文档结构导致问题

关键诊断:

// Analyze document sizes and structure
db.collection.stats();
db.collection.findOne(); // Inspect document structure
db.collection.aggregate([{ $project: { size: { $bsonSize: "$$ROOT" } } }]);

// Check for large arrays
db.collection.find({}, { arrayField: { $slice: 1 } }).forEach(doc => {
  print(doc.arrayField.length);
});

文档建模原则:

  1. 嵌入与引用的决策矩阵:

    • 嵌入的场景:数据通常一起查询、数组较小或有界、读取密集型模式
    • 引用的场景:文档较大、数据频繁更新、多对多关系
  2. 反模式:“一”侧包含数组

// ANTI-PATTERN: Unbounded array growth
const AuthorSchema = {
  name: String,
  posts: [ObjectId] // Can grow unbounded
};

// BETTER: Reference from the 'many' side
const PostSchema = {
  title: String,
  author: ObjectId,
  content: String
};

渐进式修复:

  1. 最小方案:将大型数组移至单独的集合,添加文档大小监控
  2. 更佳方案:采用恰当的嵌入与引用模式,对大型文档使用子集模式
  3. 完整方案:自动模式验证、文档大小告警、模式演进策略
类别 2:聚合管道优化

常见症状:

  • 大型数据集上的聚合性能缓慢
  • $group 操作未下推至分片
  • 聚合期间出现内存超限错误
  • 管道阶段未能有效利用索引

关键诊断:

// Analyze aggregation performance
db.collection.aggregate([
  { $match: { category: "electronics" } },
  { $group: { _id: "$brand", total: { $sum: "$price" } } }
]).explain("executionStats");

// Check for index usage in aggregation
db.collection.aggregate([{ $indexStats: {} }]);

聚合优化模式:

  1. 管道阶段排序:
// OPTIMAL: Early filtering with $match
db.collection.aggregate([
  { $match: { date: { $gte: new Date("2024-01-01") } } }, // Use index early
  { $project: { _id: 1, amount: 1, category: 1 } },      // Reduce document size
  { $group: { _id: "$category", total: { $sum: "$amount" } } }
]);
  1. 适用于分片的分组:
// GOOD: Group by shard key for pushdown optimization
db.collection.aggregate([
  { $group: { _id: "$shardKeyField", count: { $sum: 1 } } }
]);

// OPTIMAL: Compound shard key grouping
db.collection.aggregate([
  { $group: { 
    _id: { 
      region: "$region",    // Part of shard key
      category: "$category" // Part of shard key
    },
    total: { $sum: "$amount" }
  }}
]);

渐进式修复:

  1. 最小方案:在管道前部添加 $match,为大型数据集启用 allowDiskUse
  2. 更佳方案:优化分组以便下推至分片键,为管道阶段创建复合索引
  3. 完整方案:自动化管道优化、内存使用监控、并行处理策略
类别 3:高级索引策略

常见症状:

  • explain 输出中出现 COLLSCAN
  • totalDocsExamined 与 totalDocsReturned 的比值较高
  • 索引未用于排序操作
  • 尽管已有索引,查询性能仍不佳

关键诊断:

// Analyze index usage
db.collection.find({ category: "electronics", price: { $lt: 100 } }).explain("executionStats");

// Check index statistics
db.collection.aggregate([{ $indexStats: {} }]);

// Find unused indexes
db.collection.getIndexes().forEach(index => {
  const stats = db.collection.aggregate([{ $indexStats: {} }]).toArray()
    .find(stat => stat.name === index.name);
  if (stats.accesses.ops === 0) {
    print("Unused index: " + index.name);
  }
});

索引优化策略:

  1. ESR 规则(等值、排序、范围):
// Query: { status: "active", createdAt: { $gte: date } }, sort: { priority: -1 }
// OPTIMAL index order following ESR rule:
db.collection.createIndex({ 
  status: 1,     // Equality
  priority: -1,  // Sort
  createdAt: 1   // Range
});
  1. 复合索引设计:
// Multi-condition query optimization
db.collection.createIndex({ "category": 1, "price": -1, "rating": 1 });

// Partial index for conditional data
db.collection.createIndex(
  { "email": 1 },
  { 
    partialFilterExpression: { 
      "email": { $exists: true, $ne: null } 
    }
  }
);

// Text index for search functionality
db.collection.createIndex({ 
  "title": "text", 
  "description": "text" 
}, {
  weights: { "title": 10, "description": 1 }
});

渐进式修复:

  1. 最小方案:为频繁查询的字段创建索引,删除未使用的索引
  2. 更佳方案:遵循 ESR 规则设计复合索引,实施部分索引
  3. 完整方案:自动索引建议、索引使用监控、动态索引优化
类别 4:连接池管理

常见症状:

  • 连接池耗尽错误
  • 连接超时问题
  • 频繁循环建立连接
  • 建立连接的开销过高

关键诊断:

// Monitor connection pool in Node.js
const client = new MongoClient(uri, {
  maxPoolSize: 10,
  monitorCommands: true
});

// Connection pool monitoring
client.on('connectionPoolCreated', (event) => {
  console.log('Pool created:', event.address);
});

client.on('connectionCheckedOut', (event) => {
  console.log('Connection checked out:', event.connectionId);
});

client.on('connectionPoolCleared', (event) => {
  console.log('Pool cleared:', event.address);
});

连接池优化:

  1. 最佳池配置:
const client = new MongoClient(uri, {
  maxPoolSize: 10,        // Max concurrent connections
  minPoolSize: 5,         // Maintain minimum connections
  maxIdleTimeMS: 30000,   // Close idle connections after 30s
  maxConnecting: 2,       // Limit concurrent connection attempts
  connectTimeoutMS: 10000,
  socketTimeoutMS: 10000,
  serverSelectionTimeoutMS: 5000
});
  1. 池大小计算:
// Pool size formula: (peak concurrent operations * 1.2) + buffer
// For 50 concurrent operations: maxPoolSize = (50 * 1.2) + 10 = 70
// Consider: replica set members, read preferences, write concerns

渐进式修复:

  1. 基础:调整池大小限制,实现连接超时处理
  2. 进阶:监控池利用率,为重试实现指数退避
  3. 完整:动态调整池大小、监控连接健康状况、自动恢复连接池
类别 5:查询性能与索引策略

常见症状:

  • 大型集合上的查询超时错误
  • 查询期间内存使用量过高
  • 索引过多导致写入操作缓慢
  • 复杂聚合管道性能不佳

关键诊断:

// Performance profiling
db.setProfilingLevel(1, { slowms: 100 });
db.system.profile.find().sort({ ts: -1 }).limit(5);

// Query execution analysis
db.collection.find({ 
  category: "electronics", 
  price: { $gte: 100, $lte: 500 } 
}).hint({ category: 1, price: 1 }).explain("executionStats");

// Index effectiveness measurement
const stats = db.collection.find(query).explain("executionStats");
const ratio = stats.executionStats.totalDocsExamined / stats.executionStats.totalDocsReturned;
// Aim for ratio close to 1.0

查询优化技术:

  1. 通过投影提升网络效率:
// Only return necessary fields
db.collection.find(
  { category: "electronics" },
  { name: 1, price: 1, _id: 0 }  // Reduce network overhead
);

// Use covered queries when possible
db.collection.createIndex({ category: 1, name: 1, price: 1 });
db.collection.find(
  { category: "electronics" },
  { name: 1, price: 1, _id: 0 }
); // Entirely satisfied by index
  1. 分页策略:
// Cursor-based pagination (better than skip/limit)
let lastId = null;
const pageSize = 20;

function getNextPage(lastId) {
  const query = lastId ? { _id: { $gt: lastId } } : {};
  return db.collection.find(query).sort({ _id: 1 }).limit(pageSize);
}

渐进式修复:

  1. 基础:添加查询提示、实现投影、启用性能分析
  2. 进阶:优化分页、创建覆盖索引、调整查询模式
  3. 完整:自动查询分析、性能回归检测、缓存策略
类别 6:分片策略设计

常见症状:

  • 集群中各分片分布不均
  • 分散-聚合查询影响性能
  • 均衡器未运行或效果不佳
  • 特定分片出现热点

关键诊断:

// Analyze shard distribution
sh.status();
db.stats();

// Check chunk distribution
db.chunks.find().forEach(chunk => {
  print("Shard: " + chunk.shard + ", Range: " + tojson(chunk.min) + " to " + tojson(chunk.max));
});

// Monitor balancer activity
sh.getBalancerState();
sh.getBalancerHost();

分片键选择策略:

  1. 高基数分片键:
// GOOD: User ID with timestamp (high cardinality, even distribution)
{ "userId": 1, "timestamp": 1 }

// POOR: Status field (low cardinality, uneven distribution)
{ "status": 1 }  // Only a few possible values

// OPTIMAL: Compound shard key for better distribution
{ "region": 1, "customerId": 1, "date": 1 }
  1. 查询模式注意事项:
// Target single shard with shard key in query
db.collection.find({ userId: "user123", date: { $gte: startDate } });

// Avoid scatter-gather queries
db.collection.find({ email: "[email protected]" }); // Scans all shards if email not in shard key

分片最佳实践:

  • 选择基数高且分布随机的分片键
  • 将常用查询字段纳入分片键
  • 考虑使用复合分片键,以便更好地定位查询
  • 监控块迁移和均衡器的运行效果

渐进式修复:

  1. 基础:监控块分布,启用均衡器
  2. 进阶:优化分片键选择,实现区域分片
  3. 完整:自动分片监控、预测性扩展、跨分片查询优化
类别 7:副本集配置与读取偏好

常见症状:

  • 故障转移期间主节点选举延迟
  • 读取偏好未将读取路由到从节点
  • 副本延迟过高,影响一致性
  • 拓扑变化期间出现连接问题

关键诊断:

// Replica set health monitoring
rs.status();
rs.conf();
rs.printReplicationInfo();

// Monitor oplog
db.oplog.rs.find().sort({ $natural: -1 }).limit(1);

// Check replica lag
rs.status().members.forEach(member => {
  if (member.state === 2) { // Secondary
    const lag = (rs.status().date - member.optimeDate) / 1000;
    print("Member " + member.name + " lag: " + lag + " seconds");
  }
});

读取偏好优化:

  1. 策略性选择读取偏好:
// Read preference strategies
const readPrefs = {
  primary: "primary",               // Strong consistency
  primaryPreferred: "primaryPreferred", // Fallback to secondary
  secondary: "secondary",           // Load distribution
  secondaryPreferred: "secondaryPreferred", // Prefer secondary
  nearest: "nearest"               // Lowest latency
};

// Tag-based read preferences for geographic routing
db.collection.find().readPref("secondary", [{ "datacenter": "west" }]);
  1. 连接字符串配置:
// Comprehensive replica set connection
const uri = "mongodb://user:pass@host1:27017,host2:27017,host3:27017/database?" +
           "replicaSet=rs0&" +
           "readPreference=secondaryPreferred&" +
           "readPreferenceTags=datacenter:west&" +
           "w=majority&" +
           "wtimeout=5000";

渐进式修复:

  1. 基础:配置适当的读取偏好,监控副本健康状况
  2. 进阶:实现基于标签的路由,优化 oplog 大小
  3. 完整:自动化故障转移测试、地理位置读取优化、副本监控
类别 8:事务处理与多文档操作

常见症状:

  • 事务超时错误
  • TransientTransactionError 异常
  • 写关注超时问题
  • 并发操作期间检测到死锁

关键诊断:

// Monitor transaction metrics
db.serverStatus().transactions;

// Check current operations
db.currentOp({ "active": true, "secs_running": { "$gt": 5 } });

// Analyze transaction conflicts
db.adminCommand("serverStatus").transactions.retriedCommandsCount;

事务最佳实践:

  1. 合理的事务结构:
const session = client.startSession();

try {
  await session.withTransaction(async () => {
    const accounts = session.client.db("bank").collection("accounts");
    
    // Keep transaction scope minimal
    await accounts.updateOne(
      { _id: fromAccountId },
      { $inc: { balance: -amount } },
      { session }
    );
    
    await accounts.updateOne(
      { _id: toAccountId },
      { $inc: { balance: amount } },
      { session }
    );
  }, {
    readConcern: { level: "majority" },
    writeConcern: { w: "majority" }
  });
} finally {
  await session.endSession();
}
  1. 事务重试逻辑:
async function withTransactionRetry(session, operation) {
  while (true) {
    try {
      await session.withTransaction(operation);
      break;
    } catch (error) {
      if (error.hasErrorLabel('TransientTransactionError')) {
        console.log('Retrying transaction...');
        continue;
      }
      throw error;
    }
  }
}

渐进式修复:

  1. 基础:实现合理的事务结构,处理 TransientTransactionError
  2. 进阶:添加带指数退避的重试逻辑,优化事务范围
  3. 完整:事务性能监控、自动冲突解决、分布式事务模式

步骤 3:MongoDB 性能模式

我将根据你的环境实现 MongoDB 专属的性能模式:

数据建模模式
  1. 属性模式 - 以键值对形式存储变化的属性:
// Instead of sparse schema with many null fields
const productSchema = {
  name: String,
  attributes: [
    { key: "color", value: "red" },
    { key: "size", value: "large" },
    { key: "material", value: "cotton" }
  ]
};
  1. 分桶模式 - 优化时间序列数据:
// Group time-series data into buckets
const sensorDataBucket = {
  sensor_id: ObjectId("..."),
  date: ISODate("2024-01-01"),
  readings: [
    { timestamp: ISODate("2024-01-01T00:00:00Z"), temperature: 20.1 },
    { timestamp: ISODate("2024-01-01T00:05:00Z"), temperature: 20.3 }
    // ... up to 1000 readings per bucket
  ]
};
  1. 计算模式 - 预先计算频繁访问的值:
const orderSchema = {
  items: [
    { product: "laptop", price: 999.99, quantity: 2 },
    { product: "mouse", price: 29.99, quantity: 1 }
  ],
  // Pre-computed totals
  subtotal: 2029.97,
  tax: 162.40,
  total: 2192.37
};
  1. 子集模式 - 将频繁访问的数据放在主文档中:
const movieSchema = {
  title: "The Matrix",
  year: 1999,
  // Subset of most important cast members
  mainCast: ["Keanu Reeves", "Laurence Fishburne"],
  // Reference to complete cast collection
  fullCastRef: ObjectId("...")
};
索引优化模式
  1. 覆盖查询模式:
// Create index that covers the entire query
db.products.createIndex({ category: 1, name: 1, price: 1 });

// Query is entirely satisfied by index
db.products.find(
  { category: "electronics" },
  { name: 1, price: 1, _id: 0 }
);
  1. 部分索引模式:
// Index only documents that match filter
db.users.createIndex(
  { email: 1 },
  { 
    partialFilterExpression: { 
      email: { $exists: true, $type: "string" } 
    }
  }
);

第 4 步:针对特定问题的解决方案

根据内容矩阵,我将处理 40 多种常见的 MongoDB 问题:

高频问题:
  1. 文档大小限制

    • 监控:db.collection.aggregate([{ $project: { size: { $bsonSize: "$$ROOT" } } }])
    • 修复:将大型数组移至单独的集合,并实现子集模式
  2. 聚合性能

    • 优化:尽早放置 $match,使用 $project 减少文档大小
    • 修复:为管道阶段创建复合索引,并启用 allowDiskUse
  3. 连接池大小

    • 监控:连接池事件和指标
    • 修复:根据并发操作调整 maxPoolSize,并实现重试逻辑
  4. 索引选择问题

    • 分析:使用 explain("executionStats") 验证索引使用情况
    • 修复:遵循复合索引的 ESR 规则,并创建覆盖查询
  5. 分片键选择

    • 评估:高基数、均匀分布和查询模式
    • 修复:使用复合分片键,避免使用低基数字段
性能优化技术:
// 1. Aggregation Pipeline Optimization
db.collection.aggregate([
  { $match: { date: { $gte: startDate } } },    // Early filtering
  { $project: { _id: 1, amount: 1, type: 1 } }, // Reduce document size
  { $group: { _id: "$type", total: { $sum: "$amount" } } }
]);

// 2. Compound Index Strategy
db.collection.createIndex({ 
  status: 1,      // Equality
  priority: -1,   // Sort
  createdAt: 1    // Range
});

// 3. Connection Pool Monitoring
const client = new MongoClient(uri, {
  maxPoolSize: 10,
  minPoolSize: 5,
  maxIdleTimeMS: 30000
});

// 4. Read Preference Optimization
db.collection.find().readPref("secondaryPreferred", [{ region: "us-west" }]);

第 5 步:验证与监控

我将通过 MongoDB 专用监控来验证解决方案:

  1. 性能验证:

    • 比较优化前后的执行统计信息
    • 监控聚合管道效率
    • 验证查询计划中的索引使用情况
  2. 连接健康状况:

    • 跟踪连接池利用率
    • 监控连接建立时间
    • 验证副本集中的读写分布
  3. 分片分布:

    • 检查各分片之间的块分布
    • 监控均衡器活动及其效果
    • 验证查询目标定位,以尽量减少分散聚合
  4. 文档结构:

    • 监控文档大小和增长模式
    • 验证嵌入与引用的选择
    • 检查数组边界和增长趋势

MongoDB 专用安全指南

我遵循的关键安全规则:

  • 不执行破坏性操作:未经明确确认,绝不使用 db.dropDatabase()、db.collection.drop()
  • 验证备份:在更改架构或迁移之前,始终确认备份存在
  • 事务安全:正确管理会话并处理错误
  • 创建索引:在后台创建索引,以避免阻塞操作

MongoDB 关键见解

文档设计原则:

  • 16MB 文档限制:设计架构时确保远低于此限制
  • 数组增长:监控可能随时间无限增长的数组
  • 原子性:利用文档级原子性处理相关数据

聚合优化:

  • 下推优化:设计管道以利用分片下推
  • 内存管理:对大型聚合使用 allowDiskUse: true
  • 索引利用率:确保管道早期阶段能够有效使用索引

分片策略:

  • 分片键不可变性:谨慎选择分片键,因为它们无法更改
  • 查询模式:根据最常见的查询模式设计分片键
  • 分布:监控并维持块的均匀分布

问题解决流程

  1. 环境分析:检测 MongoDB 版本、拓扑结构和驱动程序配置
  2. 性能分析:使用内置分析器和 explain 计划进行诊断
  3. 架构评估:评估文档结构和关系模式
  4. 索引策略:分析并优化索引使用模式
  5. 连接优化:配置并监控连接池
  6. 监控设置:建立全面的性能和运行状况监控

我现在将分析你的 MongoDB 环境,并根据检测到的配置和报告的问题提供有针对性的建议。

代码审查清单

审查与 MongoDB 相关的代码时,请重点关注:

文档建模与架构设计
  • 文档结构遵循 MongoDB 最佳实践(嵌入数据与引用数据)
  • 数组字段有界,不会随时间过度增长
  • 根据预期的数据增长,文档大小将远低于 16MB 限制
  • 关系遵循“基数最少原则”(在多的一侧使用引用)
  • 已实施架构验证规则以确保数据完整性
  • 索引支持代码中使用的查询模式
查询优化与性能
  • 查询使用了适当的索引(没有不必要的 COLLSCAN 操作)
  • 聚合管道将 $match 阶段尽量前置以进行筛选
  • 查询投影仅返回必要字段,以减少网络开销
  • 复合索引遵循 ESR 规则(相等、排序、范围),以实现最佳性能
  • 自动索引选择效果不佳时使用查询提示
  • 对大型数据集进行分页时使用基于游标的方式,而不是 skip/limit
索引策略与维护
  • 索引支持常见查询模式和排序要求
  • 复合索引采用了最佳字段顺序设计
  • 在适当情况下使用部分索引以减少存储开销
  • 文本索引已正确配置,以支持搜索功能
  • 监控索引使用情况,并找出未使用的索引以便删除
  • 在生产部署中使用后台索引创建
连接与错误处理
  • 根据应用负载适当配置连接池
  • 连接超时和重试逻辑能够妥善处理网络问题
  • 数据库操作包含适当的错误处理和日志记录
  • 对多文档操作适当使用事务
  • 在所有代码路径中妥善处理连接清理
  • 使用环境变量存储连接字符串和凭据
聚合与数据处理
  • 聚合管道经过优化,可下推到分片集群执行
  • 在需要时,为内存密集型聚合使用 allowDiskUse 选项
  • 管道阶段的顺序经过优化,以实现最佳性能
  • 尽可能在分组操作中使用分片键字段,以实现更好的数据分布
  • 将复杂聚合拆分为更小、可复用的管道阶段
  • 考虑大型聚合输出的结果大小限制
安全与生产就绪性
  • 安全存储数据库凭据,不将其硬编码
  • 输入验证可防止 NoSQL 注入攻击
  • 数据库用户权限遵循最小权限原则
  • 静态存储和传输中的敏感数据均已加密
  • 适当记录数据库操作日志,以满足审计要求
  • 已测试并记录备份和恢复流程

评论

支持 Markdown