引言
在现代企业级应用中,数据库性能直接影响着系统的响应速度和用户体验。SQLSugar作为一款优秀的.NET ORM框架,不仅需要掌握其基本使用方法,更需要建立完整的监控与诊断体系,确保在复杂业务场景下能够快速定位和解决性能问题。本文将全面介绍SQLSugar的监控与诊断方案,帮助开发者构建高效的数据库性能管理体系。
一、SQLSugar内置监控机制
1.1 AOP事件监控体系
SQLSugar提供了完整的AOP(面向切面编程)事件机制,能够在不侵入业务代码的情况下实现全面的SQL监控。
public class SqlSugarAopLogger
{
public static void ConfigureAop(SqlSugarClient db)
{
// 开启SQL执行监控
db.Aop.OnLogExecuting = (sql, pars) =>
{
// 1. 记录原始SQL
string originalSql = sql;
// 2. 格式化参数
SugarParameter[] parameters = pars as SugarParameter[];
string formattedSql = FormatSql(originalSql, parameters);
// 3. 记录执行前日志
LogSql("SQL执行前", formattedSql, db.Ado.Connection.Database);
};
// SQL执行完成事件
db.Aop.OnLogExecuted = (sql, pars) =>
{
// 可以在此处记录执行完成状态
Console.WriteLine($"SQL执行完成: {sql}");
};
// SQL执行错误事件
db.Aop.OnError = (exp) =>
{
// 记录错误信息
LogError($"SQL执行错误: {exp.Message}", exp.Sql, db.Ado.Connection.Database);
// 可以集成到错误监控系统
SendToErrorMonitoringSystem(exp);
};
// 执行时间监控
db.Aop.OnExecutingChangeSql = (sql, pars) =>
{
// 可以修改SQL语句
return new KeyValuePair<string, SugarParameter[]>(sql, pars);
};
}
private static string FormatSql(string sql, SugarParameter[] parameters)
{
if (parameters == null || parameters.Length == 0)
return sql;
foreach (var param in parameters)
{
string paramValue = param.Value?.ToString() ?? "NULL";
sql = sql.Replace(param.ParameterName, $"'{paramValue.Replace("'", "''")}'");
}
return sql;
}
}
1.2 执行统计与性能分析
public class SqlPerformanceMonitor
{
private readonly ConcurrentDictionary<string, SqlExecutionStats> _stats =
new ConcurrentDictionary<string, SqlExecutionStats>();
public void ConfigurePerformanceMonitor(SqlSugarClient db)
{
Stopwatch stopwatch = new Stopwatch();
db.Aop.OnLogExecuting = (sql, pars) =>
{
stopwatch.Restart();
};
db.Aop.OnLogExecuted = (sql, pars) =>
{
stopwatch.Stop();
long elapsedMs = stopwatch.ElapsedMilliseconds;
// 生成SQL指纹(去除具体参数值)
string sqlFingerprint = GenerateSqlFingerprint(sql);
// 更新统计信息
var stats = _stats.GetOrAdd(sqlFingerprint,
key => new SqlExecutionStats { SqlTemplate = sqlFingerprint });
stats.TotalExecutions++;
stats.TotalDurationMs += elapsedMs;
stats.AverageDurationMs = stats.TotalDurationMs / stats.TotalExecutions;
if (elapsedMs > stats.MaxDurationMs)
stats.MaxDurationMs = elapsedMs;
if (stats.MinDurationMs == 0 || elapsedMs < stats.MinDurationMs)
stats.MinDurationMs = elapsedMs;
// 记录慢查询
if (elapsedMs > 1000) // 超过1秒定义为慢查询
{
LogSlowQuery(sql, pars, elapsedMs, db.Ado.Connection.Database);
}
};
}
public List<SqlExecutionStats> GetPerformanceReport()
{
return _stats.Values
.OrderByDescending(s => s.TotalDurationMs)
.Take(20) // 返回耗时最多的20个SQL
.ToList();
}
}
public class SqlExecutionStats
{
public string SqlTemplate { get; set; }
public long TotalExecutions { get; set; }
public long TotalDurationMs { get; set; }
public long AverageDurationMs { get; set; }
public long MaxDurationMs { get; set; }
public long MinDurationMs { get; set; }
}
二、集成第三方监控系统
2.1 集成MiniProfiler
public static class MiniProfilerIntegration
{
public static void ConfigureMiniProfiler(SqlSugarClient db)
{
db.Aop.OnLogExecuting = (sql, pars) =>
{
var profiler = MiniProfiler.Current;
if (profiler != null)
{
// 创建MiniProfiler自定义步骤
var stepKey = $"sql_{Guid.NewGuid()}";
var step = profiler.CustomTiming("sql", "执行SQL", sql);
// 将step存储在AsyncLocal中,以便在执行完成后结束
CurrentProfilerStep.Value = step;
// 添加SQL参数信息
if (pars is SugarParameter[] parameters && parameters.Length > 0)
{
var paramInfo = string.Join(", ",
parameters.Select(p => $"{p.ParameterName}={p.Value}"));
step.CommandString = $"{sql} -- 参数: {paramInfo}";
}
}
};
db.Aop.OnLogExecuted = (sql, pars) =>
{
var step = CurrentProfilerStep.Value;
if (step != null)
{
step?.Dispose();
CurrentProfilerStep.Value = null;
}
};
db.Aop.OnError = (exp) =>
{
var step = CurrentProfilerStep.Value;
if (step != null)
{
step.Errored = true;
step.Dispose();
CurrentProfilerStep.Value = null;
}
};
}
private static readonly AsyncLocal<CustomTiming> CurrentProfilerStep =
new AsyncLocal<CustomTiming>();
}
2.2 集成Application Insights
public class ApplicationInsightsIntegration
{
private readonly TelemetryClient _telemetryClient;
public ApplicationInsightsIntegration(TelemetryClient telemetryClient)
{
_telemetryClient = telemetryClient;
}
public void ConfigureAppInsights(SqlSugarClient db)
{
db.Aop.OnLogExecuting = (sql, pars) =>
{
var operationHolder = _telemetryClient.StartOperation<DependencyTelemetry>("SQL");
operationHolder.Telemetry.Type = "SQL";
operationHolder.Telemetry.Target = db.Ado.Connection.Database;
operationHolder.Telemetry.Data = sql;
CurrentOperation.Value = operationHolder;
};
db.Aop.OnLogExecuted = (sql, pars) =>
{
var operation = CurrentOperation.Value;
if (operation != null)
{
_telemetryClient.StopOperation(operation);
CurrentOperation.Value = null;
}
};
db.Aop.OnError = (exp) =>
{
var operation = CurrentOperation.Value;
if (operation != null)
{
operation.Telemetry.Success = false;
operation.Telemetry.Properties["ErrorMessage"] = exp.Message;
_telemetryClient.TrackException(exp);
_telemetryClient.StopOperation(operation);
CurrentOperation.Value = null;
}
};
}
private static readonly AsyncLocal<IOperationHolder<DependencyTelemetry>> CurrentOperation =
new AsyncLocal<IOperationHolder<DependencyTelemetry>>();
}
三、慢查询分析与优化
3.1 慢查询自动捕获系统
public class SlowQueryMonitor
{
private readonly List<SlowQueryRecord> _slowQueries = new List<SlowQueryRecord>();
private readonly object _lock = new object();
private readonly int _slowThresholdMs;
public SlowQueryMonitor(int slowThresholdMs = 1000)
{
_slowThresholdMs = slowThresholdMs;
}
public void ConfigureSlowQueryDetection(SqlSugarClient db)
{
Stopwatch stopwatch = new Stopwatch();
string currentSql = string.Empty;
SugarParameter[] currentParams = null;
db.Aop.OnLogExecuting = (sql, pars) =>
{
currentSql = sql;
currentParams = pars as SugarParameter[];
stopwatch.Restart();
};
db.Aop.OnLogExecuted = (sql, pars) =>
{
stopwatch.Stop();
long elapsedMs = stopwatch.ElapsedMilliseconds;
if (elapsedMs > _slowThresholdMs)
{
var record = new SlowQueryRecord
{
Sql = currentSql,
Parameters = currentParams,
ExecutionTime = DateTime.Now,
DurationMs = elapsedMs,
Database = db.Ado.Connection.Database,
ConnectionString = db.Ado.Connection.ConnectionString,
StackTrace = GetRelevantStackTrace()
};
lock (_lock)
{
_slowQueries.Add(record);
// 保持最近的1000条慢查询记录
if (_slowQueries.Count > 1000)
_slowQueries.RemoveAt(0);
}
// 异步通知管理员
Task.Run(() => NotifyAdministrator(record));
}
};
}
public List<SlowQueryRecord> GetSlowQueries(DateTime? from = null, DateTime? to = null)
{
lock (_lock)
{
var query = _slowQueries.AsQueryable();
if (from.HasValue)
query = query.Where(q => q.ExecutionTime >= from.Value);
if (to.HasValue)
query = query.Where(q => q.ExecutionTime <= to.Value);
return query.OrderByDescending(q => q.DurationMs).ToList();
}
}
private string GetRelevantStackTrace()
{
var stackTrace = new System.Diagnostics.StackTrace(true);
var frames = stackTrace.GetFrames();
// 只保留与业务代码相关的堆栈信息
var relevantFrames = frames
.Where(f => !f.GetMethod().DeclaringType.FullName.StartsWith("System") &&
!f.GetMethod().DeclaringType.FullName.StartsWith("Microsoft") &&
!f.GetMethod().DeclaringType.FullName.Contains("SqlSugar"))
.Take(5) // 只取前5个相关帧
.Select(f => $"{f.GetMethod().DeclaringType.FullName}.{f.GetMethod().Name}:{f.GetFileLineNumber()}");
return string.Join(Environment.NewLine, relevantFrames);
}
}
public class SlowQueryRecord
{
public string Sql { get; set; }
public SugarParameter[] Parameters { get; set; }
public DateTime ExecutionTime { get; set; }
public long DurationMs { get; set; }
public string Database { get; set; }
public string ConnectionString { get; set; }
public string StackTrace { get; set; }
}
3.2 慢查询分析报表
public class SlowQueryAnalyzer
{
public SlowQueryReport AnalyzeQueries(List<SlowQueryRecord> queries)
{
var report = new SlowQueryReport
{
TotalSlowQueries = queries.Count,
AnalysisTime = DateTime.Now
};
if (queries.Count == 0)
return report;
// 按SQL模式分组(去除具体参数值)
var sqlGroups = queries
.GroupBy(q => NormalizeSql(q.Sql))
.Select(g => new SqlPatternStats
{
SqlPattern = g.Key,
ExecutionCount = g.Count(),
AverageDuration = g.Average(q => q.DurationMs),
MaxDuration = g.Max(q => q.DurationMs),
LastExecution = g.Max(q => q.ExecutionTime)
})
.OrderByDescending(s => s.MaxDuration)
.ToList();
report.SqlPatterns = sqlGroups;
report.MostFrequentPattern = sqlGroups.OrderByDescending(s => s.ExecutionCount).FirstOrDefault();
report.SlowestPattern = sqlGroups.OrderByDescending(s => s.MaxDuration).FirstOrDefault();
// 时间分布分析
var hourlyDistribution = queries
.GroupBy(q => q.ExecutionTime.Hour)
.Select(g => new { Hour = g.Key, Count = g.Count() })
.OrderBy(x => x.Hour)
.ToList();
report.PeakHours = hourlyDistribution
.OrderByDescending(x => x.Count)
.Take(3)
.Select(x => x.Hour)
.ToList();
return report;
}
private string NormalizeSql(string sql)
{
// 去除具体参数值,只保留SQL模式
// 这是一个简化的实现,实际中可能需要更复杂的处理
string pattern = @"@\w+\d*";
return System.Text.RegularExpressions.Regex.Replace(sql, pattern, "@param");
}
}
public class SlowQueryReport
{
public int TotalSlowQueries { get; set; }
public DateTime AnalysisTime { get; set; }
public List<SqlPatternStats> SqlPatterns { get; set; }
public SqlPatternStats MostFrequentPattern { get; set; }
public SqlPatternStats SlowestPattern { get; set; }
public List<int> PeakHours { get; set; }
}
public class SqlPatternStats
{
public string SqlPattern { get; set; }
public int ExecutionCount { get; set; }
public double AverageDuration { get; set; }
public double MaxDuration { get; set; }
public DateTime LastExecution { get; set; }
}
四、连接池监控与优化
4.1 连接池状态监控
public class ConnectionPoolMonitor
{
private readonly SqlSugarClient _db;
private readonly Timer _monitorTimer;
private readonly List<ConnectionPoolMetrics> _metricsHistory = new List<ConnectionPoolMetrics>();
public ConnectionPoolMonitor(SqlSugarClient db, int monitorIntervalSeconds = 30)
{
_db = db;
_monitorTimer = new Timer(CollectMetrics, null,
TimeSpan.Zero, TimeSpan.FromSeconds(monitorIntervalSeconds));
}
private void CollectMetrics(object state)
{
try
{
var metrics = GetConnectionPoolMetrics();
_metricsHistory.Add(metrics);
// 保留最近24小时的数据
var cutoffTime = DateTime.Now.AddHours(-24);
_metricsHistory.RemoveAll(m => m.Timestamp < cutoffTime);
// 检查连接池健康状况
CheckConnectionPoolHealth(metrics);
}
catch (Exception ex)
{
Console.WriteLine($"连接池监控异常: {ex.Message}");
}
}
private ConnectionPoolMetrics GetConnectionPoolMetrics()
{
// 注意:不同数据库的连接池监控方式不同
// 这里以SQL Server为例
var metrics = new ConnectionPoolMetrics
{
Timestamp = DateTime.Now,
Database = _db.Ado.Connection.Database
};
// 在实际应用中,可能需要执行特定的系统视图查询
// 例如SQL Server: SELECT * FROM sys.dm_exec_connections
return metrics;
}
public ConnectionPoolDiagnosis GetDiagnosis()
{
if (_metricsHistory.Count < 10)
return new ConnectionPoolDiagnosis { Status = "数据不足" };
var recentMetrics = _metricsHistory
.OrderByDescending(m => m.Timestamp)
.Take(100)
.ToList();
var diagnosis = new ConnectionPoolDiagnosis
{
Status = "正常",
Suggestions = new List<string>()
};
// 分析连接数趋势
var avgConnections = recentMetrics.Average(m => m.ActiveConnections);
var maxConnections = recentMetrics.Max(m => m.ActiveConnections);
if (avgConnections > 100)
{
diagnosis.Status = "警告";
diagnosis.Suggestions.Add($"平均连接数较高: {avgConnections:F0},考虑优化连接管理");
}
if (maxConnections > 150)
{
diagnosis.Status = "警告";
diagnosis.Suggestions.Add($"最大连接数过高: {maxConnections:F0},可能存在连接泄露");
}
return diagnosis;
}
}
public class ConnectionPoolMetrics
{
public DateTime Timestamp { get; set; }
public string Database { get; set; }
public int ActiveConnections { get; set; }
public int IdleConnections { get; set; }
public int ConnectionTimeouts { get; set; }
public double AvgConnectionWaitTime { get; set; }
}
public class ConnectionPoolDiagnosis
{
public string Status { get; set; }
public List<string> Suggestions { get; set; }
}
五、实时性能仪表板
5.1 Web监控界面
<!-- PerformanceDashboard.html -->
<!DOCTYPE html>
<html>
<head>
<title>SQLSugar性能监控仪表板</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
.dashboard {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
padding: 20px;
}
.metric-card {
background: white;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.slow-query-list {
max-height: 400px;
overflow-y: auto;
}
</style>
</head>
<body>
<div class="dashboard">
<!-- QPS监控 -->
<div class="metric-card">
<h3>QPS监控</h3>
<canvas id="qpsChart"></canvas>
</div>
<!-- 慢查询统计 -->
<div class="metric-card">
<h3>慢查询统计</h3>
<canvas id="slowQueryChart"></canvas>
</div>
<!-- 连接池状态 -->
<div class="metric-card">
<h3>连接池状态</h3>
<div id="connectionPoolStatus"></div>
</div>
<!-- 最近慢查询 -->
<div class="metric-card">
<h3>最近慢查询</h3>
<div id="recentSlowQueries" class="slow-query-list"></div>
</div>
</div>
<script>
// 实时数据更新
async function updateDashboard() {
const response = await fetch('/api/sqlsugar-metrics');
const data = await response.json();
updateQPSChart(data.qpsData);
updateSlowQueryChart(data.slowQueryData);
updateConnectionPool(data.connectionPool);
updateSlowQueries(data.recentSlowQueries);
}
setInterval(updateDashboard, 5000);
updateDashboard(); // 初始加载
</script>
</body>
</html>
SQLSugar监控与诊断体系的建立是一个系统性工程,需要从多个层面进行考虑:
-
监控层面:利用SQLSugar的AOP机制实现全面的SQL监控
-
分析层面:建立慢查询分析、性能统计和连接池监控
-
可视化层面:通过仪表板实时展示关键指标
-
告警层面:建立智能告警机制,及时发现潜在问题
-
集成层面:与现有监控系统(如Application Insights、MiniProfiler)集成
在实际应用中,建议根据以下原则进行配置:
-
分级监控:根据环境(开发、测试、生产)配置不同级别的监控
-
性能开销:监控本身会带来性能开销,需要权衡监控粒度和性能影响
-
自动化处理:对常见问题建立自动化处理机制
-
持续优化:定期分析监控数据,持续优化SQL和数据库设计
126

被折叠的 条评论
为什么被折叠?



