第一章:MongoDB Profiler核心原理

“Profiler不是日志,而是数据库的X光机”

1.1 Profiler的工作机制

// MongoDB Profiler配置示例
// 开启profiling并设置级别
db.setProfilingLevel(2, 10); // 级别2记录所有操作,慢查询阈值设为10ms

// 查询Profiler数据
var profilerData = db.system.profile.find({
    millis: { $gt: 50 }, // 筛选耗时超过50ms的操作
    op: { $in: ["query", "update"] } // 仅关注查询和更新操作
}).sort({ ts: -1 }).limit(20);

// 输出关键性能指标
profilerData.forEach(doc => {
    console.log(`操作类型: ${doc.op} | ` +
               `耗时: ${doc.millis}ms | ` +
               `查询语句: ${JSON.stringify(doc.query)} | ` +
               `锁类型: ${doc.locks.w} | ` +
               `索引使用: ${doc.queryPlan || "未使用索引"}`);
});

技术要点:

  • millis字段记录操作耗时
  • locks.w表示写锁等待时间
  • queryPlan显示查询执行计划

第二章:实战:定位慢查询

“90%的性能问题都藏在慢查询里”

2.1 慢查询诊断流程

// 1. 定位高频慢查询
var slowQueries = db.system.profile.aggregate([
    { $match: { millis: { $gt: 100 } } },
    { $group: {
        _id: "$query",
        count: { $sum: 1 },
        avgTime: { $avg: "$millis" }
    }},
    { $sort: { count: -1, avgTime: -1 } }
]).toArray();

// 2. 使用explain分析执行计划
slowQueries.forEach(query => {
    var explainResult = db.collection.find(query._id).explain("executionStats");
    
    if (explainResult.queryPlanner.winningPlan.stage === "COLLSCAN") {
        console.warn(`发现全表扫描!建议为字段${query._id}创建索引`);
    }
    
    console.log(`索引使用情况: ${explainResult.queryPlanner.winningPlan.indexName}`);
    console.log(`扫描文档数: ${explainResult.executionStats.totalDocsExamined}`);
});

// 3. 创建优化索引
db.collection.createIndex({ 
    "user_id": 1, 
    "created_at": -1 
}, {
    name: "user_activity_index",
    background: true // 后台创建避免阻塞
});

性能优化:

  • 使用COLLSCAN标识全表扫描
  • background: true防止索引创建阻塞
  • executionStats提供详细执行数据

第三章:锁竞争分析

“锁等待是并发性能的隐形杀手”

3.1 锁竞争检测

// 分析锁等待时间
db.system.profile.aggregate([
    { $match: { locks: { $exists: true } } },
    { $project: {
        writeLockWait: "$locks.w.acquireWaitMillis",
        query: 1
    }},
    { $match: { writeLockWait: { $gt: 10 } } },
    { $sort: { writeLockWait: -1 } },
    { $limit: 10 }
]).forEach(doc => {
    console.log(`锁等待: ${doc.writeLockWait}ms | 查询: ${JSON.stringify(doc.query)}`);
});

// 锁类型统计
var lockStats = db.system.profile.aggregate([
    { $match: { locks: { $exists: true } } },
    { $group: {
        _id: "$locks.w.type",
        count: { $sum: 1 },
        avgWait: { $avg: "$locks.w.acquireWaitMillis" }
    }}
]);

lockStats.forEach(stat => {
    console.log(`锁类型: ${stat._id} | ` +
               `出现次数: ${stat.count} | ` +
               `平均等待: ${stat.avgWait}ms`);
});

解决方案:

  • 优化事务粒度减少锁持有时间
  • 对热点数据进行分片
  • 使用读写分离架构

第四章:索引使用深度分析

“索引不是越多越好,而是越准越好”

4.1 索引有效性验证

// 统计索引使用情况
db.system.profile.aggregate([
    { $group: {
        _id: "$queryPlan.indexName",
        usedCount: { $sum: 1 },
        totalQueryTime: { $sum: "$millis" }
    }},
    { $sort: { usedCount: -1 } }
]).forEach(index => {
    console.log(`索引: ${index._id} | ` +
               `使用次数: ${index.usedCount} | ` +
               `总耗时: ${index.totalQueryTime}ms`);
});

// 识别未使用索引
db.collection.stats().indexes.forEach(index => {
    if (!index.name.startsWith("_id_") && 
        db.system.profile.find({ "queryPlan.indexName": index.name }).count() === 0) {
        console.warn(`发现未使用索引: ${index.name}`);
    }
});

优化建议:

  • 删除未使用的冗余索引
  • 使用覆盖索引减少IO
  • 对复合查询创建复合索引

第五章:事务性能调优

“ACID特性不能牺牲性能”

5.1 事务性能分析

// 分析事务耗时
db.system.profile.find({
    op: "command",
    "command.writeConcern": { $exists: true }
}).sort({ millis: -1 }).limit(5).forEach(tx => {
    console.log(`事务耗时: ${tx.millis}ms | ` +
               `写关注: ${JSON.stringify(tx.command.writeConcern)} | ` +
               `操作数: ${tx.command.ops.length}`);
});

// 优化事务粒度
function optimizeTransaction() {
    const session = db.getMongo().startSession();
    session.startTransaction();
    
    try {
        const coll = session.getDatabase("test").orders;
        
        // 集中处理多个操作
        coll.insertOne({ order_id: 123, items: 10 });
        coll.updateOne({ order_id: 123 }, { $inc: { total: 100 } });
        
        session.commitTransaction();
    } catch (e) {
        session.abortTransaction();
        throw e;
    } finally {
        session.endSession();
    }
}

关键指标:

  • writeConcern影响提交速度
  • 事务操作数与性能成反比
  • 使用批处理减少网络往返

第六章:分片集群性能分析

“分片不是万能药,而是需要精细调优”

6.1 分片均衡性检查

// 检查分片数据分布
sh.status().chunksInfo.forEach(chunk => {
    console.log(`分片: ${chunk.shard} | ` +
               `范围: ${chunk.min}~${chunk.max} | ` +
               `文档数: ${chunk.count}`);
});

// 查询路由性能
db.system.profile.find({
    op: "query",
    "command.shards": { $exists: true }
}).forEach(query => {
    console.log(`分片查询耗时: ${query.millis}ms | ` +
               `涉及分片数: ${query.command.shards.length}`);
});

优化策略:

  • 重新平衡数据分布
  • 优化分片键选择
  • 使用标签分片控制数据分布

第七章:真实案例解析

“纸上得来终觉浅,绝知此事要躬行”

7.1 电商秒杀场景优化

// 原始慢查询
db.orders.find({
    product_id: 1001,
    status: "pending",
    created_at: { $gt: ISODate("2025-08-20T00:00:00Z") }
}).sort({ created_at: -1 });

// Profiler发现:未使用索引导致全表扫描

// 优化方案
db.orders.createIndex({
    product_id: 1,
    status: 1,
    created_at: -1
}, {
    name: "product_status_date_index"
});

// 优化后查询
db.orders.find({
    product_id: 1001,
    status: "pending",
    created_at: { $gt: ISODate("2025-08-20T00:00:00Z") }
}).hint("product_status_date_index").sort({ created_at: -1 });

性能对比:

指标 优化前 优化后
查询耗时 1200ms 15ms
扫描文档数 120万 1000
锁等待时间 80ms 2ms

第八章:自动化监控体系构建

“人工巡检不如智能监控”

8.1 自动化报警系统

// 自动化监控脚本
function monitorPerformance() {
    const slowQueries = db.system.profile.find({
        millis: { $gt: 100 },
        ts: { $gte: new Date(new Date() - 5 * 60 * 1000) }
    }).count();
    
    if (slowQueries > 5) {
        sendAlert(`发现${slowQueries}个慢查询,请立即检查!`);
    }
    
    const lockWait = db.system.profile.aggregate([
        { $match: { "locks.w.acquireWaitMillis": { $gt: 10 } } },
        { $group: { _id: null, avgWait: { $avg: "$locks.w.acquireWaitMillis" } } }
    ]).toArray()[0];
    
    if (lockWait && lockWait.avgWait > 20) {
        sendAlert(`平均锁等待时间${lockWait.avgWait.toFixed(2)}ms,可能存在锁竞争!`);
    }
}

// 定时任务配置
scheduleJob(monitorPerformance, 60 * 1000); // 每分钟检查一次

监控维度:

  • 慢查询数量
  • 锁等待时间
  • 索引命中率
  • 内存使用率

构建性能分析闭环

“性能优化不是一次性工程,而是持续改进的过程”

优化阶段 工具/方法
问题发现 Profiler + Explain
根因分析 执行计划 + 锁统计
方案验证 A/B测试 + 压力测试
持续监控 自动化脚本 + 报警系统

终极建议:

  • 将Profiler纳入日常运维流程
  • 建立性能基线进行对比分析
  • 定期进行索引和分片优化
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐