Files
MyGoNavi/internal/app/methods_query_history.go
Syngnat a74065bdbb feat(explain): 新增慢 SQL 历史存储与 DBQueryMulti 执行埋点
- 存储层:JSONL 单连接单文件,5MB 自动滚动,TopN 排序 + SQL 指纹去重
- 执行埋点:DBQueryMulti 用 named return + defer 异步记录,成功后自动写入历史
- 阈值过滤:默认 500ms 以下查询跳过记录,避免历史爆炸
- 查询入口:GetSlowQueries 按 duration/rowsRead/recent 排序,ClearSlowQueries 支持清空
- SQL 指纹:字面量/大小写归一化后 sha256,同模板不同参数视为同一查询
- 测试覆盖:新增 13 个单元测试覆盖存储/滚动/排序/去重/指纹
2026-06-19 13:17:39 +08:00

72 lines
2.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package app
import (
"strings"
"GoNavi-Wails/internal/connection"
"GoNavi-Wails/internal/logger"
)
// 慢 SQL 历史的 Wails 绑定入口。
//
// 前端调用:
// - GetSlowQueries(connectionId, dbName, sortBy, limit) → []QueryExecutionRecord
// - ClearSlowQueries(connectionId, dbName) → 错误(清空当前连接的历史)
//
// sortBy: "duration" | "rowsRead" | "recent"
// GetSlowQueries 返回当前连接的慢 SQL 历史按指定字段排序、SQL 指纹去重后取前 N。
// limit <= 0 时返回前 100 条。
func (a *App) GetSlowQueries(config connection.ConnectionConfig, dbName, sortBy string, limit int) connection.QueryResult {
runConfig := normalizeRunConfig(config, dbName)
connFP, ok := buildConnectionFingerprint(runConfig)
if !ok || connFP == "" {
return connection.QueryResult{Success: false, Message: "无法解析连接指纹"}
}
if limit <= 0 {
limit = 100
}
store := newQueryHistoryStore(a.configDir, connFP)
records, err := store.LoadTopN(strings.TrimSpace(sortBy), limit, true)
if err != nil {
logger.Warnf("GetSlowQueries 加载失败connFp=%s err=%v", connFP, err)
return connection.QueryResult{Success: false, Message: err.Error()}
}
return connection.QueryResult{Success: true, Message: "加载完成", Data: records}
}
// ClearSlowQueries 清空当前连接的慢 SQL 历史。
// 删除主文件 + rotate 文件(.1)。
func (a *App) ClearSlowQueries(config connection.ConnectionConfig, dbName string) connection.QueryResult {
runConfig := normalizeRunConfig(config, dbName)
connFP, ok := buildConnectionFingerprint(runConfig)
if !ok || connFP == "" {
return connection.QueryResult{Success: false, Message: "无法解析连接指纹"}
}
store := newQueryHistoryStore(a.configDir, connFP)
if err := store.Clear(); err != nil {
logger.Warnf("ClearSlowQueries 失败connFp=%s err=%v", connFP, err)
return connection.QueryResult{Success: false, Message: err.Error()}
}
return connection.QueryResult{Success: true, Message: "已清空慢查询历史"}
}
// recordQueryExecutionAsync 异步追加一条慢查询记录,不阻塞主查询返回。
// 调用方应传入已计算的 durationMs 和 rowsRead/Returned。
func (a *App) recordQueryExecutionAsync(config connection.ConnectionConfig, dbType, sql string, durationMs, rowsRead, rowsReturned int64) {
if durationMs < queryHistorySlowThresholdMs {
return
}
record := buildQueryExecutionRecord(config, dbType, sql, durationMs, rowsRead, rowsReturned)
go func() {
defer func() {
if r := recover(); r != nil {
logger.Warnf("recordQueryExecutionAsync panic%v", r)
}
}()
store := newQueryHistoryStore(a.configDir, record.ConnectionFP)
store.Append(record)
}()
}