1. 监控数据库性能的必要性

在咱们开发电商系统时,某个促销日凌晨突然出现数据库响应延迟。当时手忙脚乱地登录服务器查看日志,才发现是某个未优化的聚合查询拖垮了整个集群。这种惨痛经历让我意识到:主动监控数据库性能就像给系统装"心电图",能提前发现潜在风险。

2. 环境准备与基础配置

2.1 所需技术栈

  • .NET 6+ 运行环境
  • MongoDB.Driver 2.18+ 驱动
  • MongoDB 5.0+ 数据库

2.2 连接数据库的正确姿势

// 创建MongoClient时启用性能监控
var settings = new MongoClientSettings {
    Server = new MongoServerAddress("localhost", 27017),
    ClusterConfigurator = cb => {
        // 启用命令事件监听(性能监控的关键配置)
        cb.Subscribe<CommandStartedEvent>(e => {
            Console.WriteLine($"命令开始: {e.CommandName}");
        });
        cb.Subscribe<CommandSucceededEvent>(e => {
            Console.WriteLine($"命令成功: {e.CommandName} 耗时 {e.Duration.TotalMilliseconds}ms");
        });
    }
};
var client = new MongoClient(settings);
var database = client.GetDatabase("shopDB");

3. 核心性能指标监控实现

3.1 实时慢查询捕获

// 设置慢查询阈值(单位:毫秒)
var profileFilter = new BsonDocument("millis", new BsonDocument("$gt", 100));
var options = new CreateCollectionOptions {
    Capped = true,
    MaxSize = 1024 * 1024 // 1MB容量
};

// 创建性能分析集合
database.CreateCollection("system.profile", options);
database.SetProfilingLevel(ProfilingLevel.All, 100);

// 实时监听慢查询
var profileCollection = database.GetCollection<BsonDocument>("system.profile");
var pipeline = new EmptyPipelineDefinition<BsonDocument>()
    .Match("{ op: { $ne: 'command' }, millis: { $gt: 100 } }");

using var cursor = profileCollection.Watch(pipeline);
while (cursor.MoveNext())
{
    foreach (var change in cursor.Current)
    {
        var query = change["command"]?.AsBsonDocument;
        Console.WriteLine($"慢查询告警:{query}");
    }
}

3.2 连接池健康检查

// 获取连接池状态
var server = client.Cluster.SelectServer(new SelectServerOptions());
var connectionPool = server.GetConnectionPool();

var poolStats = new BsonDocument {
    { "current", connectionPool.AvailableCount },
    { "inUse", connectionPool.UsedCount },
    { "maxSize", connectionPool.MaxConnectionPoolSize }
};

Console.WriteLine($"连接池状态:{poolStats}");

// 当连接数超过90%时触发告警
if ((double)poolStats["inUse"] / poolStats["maxSize"] > 0.9)
{
    Console.WriteLine("⚠️ 连接池即将耗尽!");
}

4. 高级监控技巧

4.1 索引使用情况分析

var indexesCollection = database.GetCollection<BsonDocument>("products");
var indexes = await (await indexesCollection.Indexes.ListAsync()).ToListAsync();

foreach (var index in indexes)
{
    var statsCommand = new BsonDocument {
        { "collStats", "products" },
        { "indexDetails", true }
    };
    
    var stats = await database.RunCommandAsync<BsonDocument>(statsCommand);
    var indexStats = stats["indexDetails"][index["name"]];
    
    Console.WriteLine($"索引 {index["name"]} 使用次数:{indexStats["accesses"]["ops"]}");
}

4.2 内存使用监控

var serverStatus = await database.RunCommandAsync<BsonDocument>(new BsonDocument {
    { "serverStatus", 1 },
    { "mem", 1 },
    { "wiredTiger", 1 }
});

var memoryUsage = new {
    Resident = serverStatus["mem"]["resident"].AsInt32,
    Virtual = serverStatus["mem"]["virtual"].AsInt32,
    CacheUsage = serverStatus["wiredTiger"]["cache"]["bytes currently in cache"].AsInt64
};

Console.WriteLine($"内存使用:物理 {memoryUsage.Resident}MB / 虚拟 {memoryUsage.Virtual}MB");
Console.WriteLine($"缓存使用:{memoryUsage.CacheUsage / 1024 / 1024}MB");

5. 实战应用场景

5.1 秒杀场景优化

在某次秒杀活动中,我们通过实时监控发现某个商品详情的查询耗时突然从5ms飙升到200ms。经分析发现是未命中索引导致的全表扫描,及时添加复合索引后性能恢复。

5.2 连接池泄漏排查

某服务重启后持续出现连接超时,通过监控发现连接池使用率始终维持在100%。最终定位到某个未关闭的游标导致的连接泄漏,增加using语句后问题解决。

6. 技术方案优缺点

优势:

  1. 深度集成现有C#系统
  2. 可自定义监控指标
  3. 实时性高(毫秒级响应)

不足:

  1. 数据可视化需要二次开发
  2. 长期存储需自行实现
  3. 可能影响生产性能(需谨慎配置)

7. 实施注意事项

  1. 监控采样频率建议设置在5-10秒
  2. 生产环境慎用全量命令监控
  3. 确保监控账号具有clusterMonitor权限
  4. 使用单独的监控数据库防止日志膨胀
  5. 设置合理的熔断机制避免监控影响业务

8. 总结与展望

通过MongoDB.Driver实现性能监控,我们就像给数据库装上了"听诊器"。虽然需要一定的开发成本,但获得的灵活性和深度集成优势是第三方工具无法比拟的。未来可结合Prometheus实现指标存储,或集成Grafana打造可视化看板。