diff --git a/internal/models/metric.go b/internal/models/metric.go index 40f3813..4cb427a 100644 --- a/internal/models/metric.go +++ b/internal/models/metric.go @@ -3,12 +3,12 @@ package models // CPUMetric CPU指标 type CPUMetric struct { ID uint `gorm:"primaryKey;autoIncrement" json:"id"` - AgentID string `gorm:"index:idx_agent_time" json:"agentId"` // 探针ID - UsagePercent float64 `json:"usagePercent"` // CPU使用率 - LogicalCores int `json:"logicalCores"` // 逻辑核心数 - PhysicalCores int `json:"physicalCores"` // 物理核心数 - ModelName string `json:"modelName"` // CPU型号 - Timestamp int64 `gorm:"index:idx_agent_time;index:idx_time" json:"timestamp"` // 时间戳(毫秒) + AgentID string `gorm:"index:idx_cpu_agent_ts,priority:1" json:"agentId"` // 探针ID + UsagePercent float64 `json:"usagePercent"` // CPU使用率 + LogicalCores int `json:"logicalCores"` // 逻辑核心数 + PhysicalCores int `json:"physicalCores"` // 物理核心数 + ModelName string `json:"modelName"` // CPU型号 + Timestamp int64 `gorm:"index:idx_cpu_agent_ts,priority:2;index:idx_cpu_ts" json:"timestamp"` // 时间戳(毫秒) } func (CPUMetric) TableName() string { @@ -18,15 +18,15 @@ func (CPUMetric) TableName() string { // MemoryMetric 内存指标 type MemoryMetric struct { ID uint `gorm:"primaryKey;autoIncrement" json:"id"` - AgentID string `gorm:"index:idx_agent_time" json:"agentId"` // 探针ID - Total uint64 `json:"total"` // 总内存(字节) - Used uint64 `json:"used"` // 已使用(字节) - Free uint64 `json:"free"` // 空闲(字节) - UsagePercent float64 `json:"usagePercent"` // 使用率 - SwapTotal uint64 `json:"swapTotal"` // 总交换空间(字节) - SwapUsed uint64 `json:"swapUsed"` // 已使用交换空间(字节) - SwapFree uint64 `json:"swapFree"` // 空闲交换空间(字节) - Timestamp int64 `gorm:"index:idx_agent_time;index:idx_time" json:"timestamp"` // 时间戳(毫秒) + AgentID string `gorm:"index:idx_mem_agent_ts,priority:1" json:"agentId"` // 探针ID + Total uint64 `json:"total"` // 总内存(字节) + Used uint64 `json:"used"` // 已使用(字节) + Free uint64 `json:"free"` // 空闲(字节) + UsagePercent float64 `json:"usagePercent"` // 使用率 + SwapTotal uint64 `json:"swapTotal"` // 总交换空间(字节) + SwapUsed uint64 `json:"swapUsed"` // 已使用交换空间(字节) + SwapFree uint64 `json:"swapFree"` // 空闲交换空间(字节) + Timestamp int64 `gorm:"index:idx_mem_agent_ts,priority:2;index:idx_mem_ts" json:"timestamp"` // 时间戳(毫秒) } func (MemoryMetric) TableName() string { @@ -36,13 +36,13 @@ func (MemoryMetric) TableName() string { // DiskMetric 磁盘指标 type DiskMetric struct { ID uint `gorm:"primaryKey;autoIncrement" json:"id"` - AgentID string `gorm:"index:idx_agent_time" json:"agentId"` // 探针ID - MountPoint string `json:"mountPoint"` // 挂载点 - Total uint64 `json:"total"` // 总容量(字节) - Used uint64 `json:"used"` // 已使用(字节) - Free uint64 `json:"free"` // 空闲(字节) - UsagePercent float64 `json:"usagePercent"` // 使用率 - Timestamp int64 `gorm:"index:idx_agent_time;index:idx_time" json:"timestamp"` // 时间戳(毫秒) + AgentID string `gorm:"index:idx_disk_agent_ts,priority:1" json:"agentId"` // 探针ID + MountPoint string `json:"mountPoint"` // 挂载点 + Total uint64 `json:"total"` // 总容量(字节) + Used uint64 `json:"used"` // 已使用(字节) + Free uint64 `json:"free"` // 空闲(字节) + UsagePercent float64 `json:"usagePercent"` // 使用率 + Timestamp int64 `gorm:"index:idx_disk_agent_ts,priority:2;index:idx_disk_ts" json:"timestamp"` // 时间戳(毫秒) } func (DiskMetric) TableName() string { @@ -52,13 +52,13 @@ func (DiskMetric) TableName() string { // NetworkMetric 网络指标 type NetworkMetric struct { ID uint `gorm:"primaryKey;autoIncrement" json:"id"` - AgentID string `gorm:"index:idx_agent_time" json:"agentId"` // 探针ID - Interface string `json:"interface"` // 网卡名称 - BytesSentRate uint64 `json:"bytesSentRate"` // 发送速率(字节/秒) - BytesRecvRate uint64 `json:"bytesRecvRate"` // 接收速率(字节/秒) - BytesSentTotal uint64 `json:"bytesSentTotal"` // 累计发送字节数 - BytesRecvTotal uint64 `json:"bytesRecvTotal"` // 累计接收字节数 - Timestamp int64 `gorm:"index:idx_agent_time;index:idx_time" json:"timestamp"` // 时间戳(毫秒) + AgentID string `gorm:"index:idx_net_agent_ts,priority:1" json:"agentId"` // 探针ID + Interface string `json:"interface"` // 网卡名称 + BytesSentRate uint64 `json:"bytesSentRate"` // 发送速率(字节/秒) + BytesRecvRate uint64 `json:"bytesRecvRate"` // 接收速率(字节/秒) + BytesSentTotal uint64 `json:"bytesSentTotal"` // 累计发送字节数 + BytesRecvTotal uint64 `json:"bytesRecvTotal"` // 累计接收字节数 + Timestamp int64 `gorm:"index:idx_net_agent_ts,priority:2;index:idx_net_ts" json:"timestamp"` // 时间戳(毫秒) } func (NetworkMetric) TableName() string { @@ -68,11 +68,11 @@ func (NetworkMetric) TableName() string { // LoadMetric 系统负载指标 type LoadMetric struct { ID uint `gorm:"primaryKey;autoIncrement" json:"id"` - AgentID string `gorm:"index:idx_agent_time" json:"agentId"` // 探针ID - Load1 float64 `json:"load1"` // 1分钟负载 - Load5 float64 `json:"load5"` // 5分钟负载 - Load15 float64 `json:"load15"` // 15分钟负载 - Timestamp int64 `gorm:"index:idx_agent_time;index:idx_time" json:"timestamp"` // 时间戳(毫秒) + AgentID string `gorm:"index:idx_load_agent_ts,priority:1" json:"agentId"` // 探针ID + Load1 float64 `json:"load1"` // 1分钟负载 + Load5 float64 `json:"load5"` // 5分钟负载 + Load15 float64 `json:"load15"` // 15分钟负载 + Timestamp int64 `gorm:"index:idx_load_agent_ts,priority:2;index:idx_load_ts" json:"timestamp"` // 时间戳(毫秒) } func (LoadMetric) TableName() string { @@ -82,19 +82,19 @@ func (LoadMetric) TableName() string { // DiskIOMetric 磁盘IO指标 type DiskIOMetric struct { ID uint `gorm:"primaryKey;autoIncrement" json:"id"` - AgentID string `gorm:"index:idx_agent_time" json:"agentId"` // 探针ID - Device string `json:"device"` // 设备名称 - ReadCount uint64 `json:"readCount"` // 读取次数 - WriteCount uint64 `json:"writeCount"` // 写入次数 - ReadBytes uint64 `json:"readBytes"` // 读取字节数 - WriteBytes uint64 `json:"writeBytes"` // 写入字节数 - ReadBytesRate uint64 `json:"readBytesRate"` // 读取速率(字节/秒) - WriteBytesRate uint64 `json:"writeBytesRate"` // 写入速率(字节/秒) - ReadTime uint64 `json:"readTime"` // 读取时间(毫秒) - WriteTime uint64 `json:"writeTime"` // 写入时间(毫秒) - IoTime uint64 `json:"ioTime"` // IO时间(毫秒) - IopsInProgress uint64 `json:"iopsInProgress"` // 正在进行的IO操作数 - Timestamp int64 `gorm:"index:idx_agent_time;index:idx_time" json:"timestamp"` // 时间戳(毫秒) + AgentID string `gorm:"index:idx_diskio_agent_ts,priority:1" json:"agentId"` // 探针ID + Device string `json:"device"` // 设备名称 + ReadCount uint64 `json:"readCount"` // 读取次数 + WriteCount uint64 `json:"writeCount"` // 写入次数 + ReadBytes uint64 `json:"readBytes"` // 读取字节数 + WriteBytes uint64 `json:"writeBytes"` // 写入字节数 + ReadBytesRate uint64 `json:"readBytesRate"` // 读取速率(字节/秒) + WriteBytesRate uint64 `json:"writeBytesRate"` // 写入速率(字节/秒) + ReadTime uint64 `json:"readTime"` // 读取时间(毫秒) + WriteTime uint64 `json:"writeTime"` // 写入时间(毫秒) + IoTime uint64 `json:"ioTime"` // IO时间(毫秒) + IopsInProgress uint64 `json:"iopsInProgress"` // 正在进行的IO操作数 + Timestamp int64 `gorm:"index:idx_diskio_agent_ts,priority:2;index:idx_diskio_ts" json:"timestamp"` // 时间戳(毫秒) } func (DiskIOMetric) TableName() string { @@ -104,18 +104,18 @@ func (DiskIOMetric) TableName() string { // GPUMetric GPU指标 type GPUMetric struct { ID uint `gorm:"primaryKey;autoIncrement" json:"id"` - AgentID string `gorm:"index:idx_agent_time" json:"agentId"` // 探针ID - Index int `json:"index"` // GPU索引 - Name string `json:"name"` // GPU名称 - Utilization float64 `json:"utilization"` // GPU使用率(%) - MemoryTotal uint64 `json:"memoryTotal"` // 总显存(字节) - MemoryUsed uint64 `json:"memoryUsed"` // 已使用显存(字节) - MemoryFree uint64 `json:"memoryFree"` // 空闲显存(字节) - Temperature float64 `json:"temperature"` // 温度(℃) - PowerDraw float64 `json:"powerDraw"` // 功耗(瓦) - FanSpeed float64 `json:"fanSpeed"` // 风扇转速(%) - PerformanceState string `json:"performanceState"` // 性能状态 - Timestamp int64 `gorm:"index:idx_agent_time;index:idx_time" json:"timestamp"` // 时间戳(毫秒) + AgentID string `gorm:"index:idx_gpu_agent_idx_ts,priority:1" json:"agentId"` // 探针ID + Index int `gorm:"index:idx_gpu_agent_idx_ts,priority:2" json:"index"` // GPU索引 + Name string `json:"name"` // GPU名称 + Utilization float64 `json:"utilization"` // GPU使用率(%) + MemoryTotal uint64 `json:"memoryTotal"` // 总显存(字节) + MemoryUsed uint64 `json:"memoryUsed"` // 已使用显存(字节) + MemoryFree uint64 `json:"memoryFree"` // 空闲显存(字节) + Temperature float64 `json:"temperature"` // 温度(℃) + PowerDraw float64 `json:"powerDraw"` // 功耗(瓦) + FanSpeed float64 `json:"fanSpeed"` // 风扇转速(%) + PerformanceState string `json:"performanceState"` // 性能状态 + Timestamp int64 `gorm:"index:idx_gpu_agent_idx_ts,priority:3;index:idx_gpu_ts" json:"timestamp"` // 时间戳(毫秒) } func (GPUMetric) TableName() string { @@ -125,11 +125,11 @@ func (GPUMetric) TableName() string { // TemperatureMetric 温度指标 type TemperatureMetric struct { ID uint `gorm:"primaryKey;autoIncrement" json:"id"` - AgentID string `gorm:"index:idx_agent_time" json:"agentId"` // 探针ID - SensorKey string `json:"sensorKey"` // 传感器标识 - SensorLabel string `json:"sensorLabel"` // 传感器标签 - Temperature float64 `json:"temperature"` // 温度(℃) - Timestamp int64 `gorm:"index:idx_agent_time;index:idx_time" json:"timestamp"` // 时间戳(毫秒) + AgentID string `gorm:"index:idx_temp_agent_sensor_ts,priority:1" json:"agentId"` // 探针ID + SensorKey string `gorm:"index:idx_temp_agent_sensor_ts,priority:2" json:"sensorKey"` // 传感器标识 + SensorLabel string `json:"sensorLabel"` // 传感器标签 + Temperature float64 `json:"temperature"` // 温度(℃) + Timestamp int64 `gorm:"index:idx_temp_agent_sensor_ts,priority:3;index:idx_temp_ts" json:"timestamp"` // 时间戳(毫秒) } func (TemperatureMetric) TableName() string { @@ -139,16 +139,16 @@ func (TemperatureMetric) TableName() string { // HostMetric 主机信息指标 type HostMetric struct { ID uint `gorm:"primaryKey;autoIncrement" json:"id"` - AgentID string `gorm:"index:idx_agent_time" json:"agentId"` // 探针ID - OS string `json:"os"` // 操作系统 - Platform string `json:"platform"` // 平台 - PlatformVersion string `json:"platformVersion"` // 平台版本 - KernelVersion string `json:"kernelVersion"` // 内核版本 - KernelArch string `json:"kernelArch"` // 内核架构 - Uptime uint64 `json:"uptime"` // 运行时间(秒) - BootTime uint64 `json:"bootTime"` // 启动时间(Unix时间戳-秒) - Procs uint64 `json:"procs"` // 进程数 - Timestamp int64 `gorm:"index:idx_agent_time;index:idx_time" json:"timestamp"` // 时间戳(毫秒) + AgentID string `gorm:"uniqueIndex:ux_host_agent" json:"agentId"` // 探针ID(唯一约束用于 upsert) + OS string `json:"os"` // 操作系统 + Platform string `json:"platform"` // 平台 + PlatformVersion string `json:"platformVersion"` // 平台版本 + KernelVersion string `json:"kernelVersion"` // 内核版本 + KernelArch string `json:"kernelArch"` // 内核架构 + Uptime uint64 `json:"uptime"` // 运行时间(秒) + BootTime uint64 `json:"bootTime"` // 启动时间(Unix时间戳-秒) + Procs uint64 `json:"procs"` // 进程数 + Timestamp int64 `gorm:"index:idx_host_ts" json:"timestamp"` // 时间戳(毫秒) } func (HostMetric) TableName() string { @@ -158,19 +158,19 @@ func (HostMetric) TableName() string { // MonitorMetric 监控指标 type MonitorMetric struct { ID uint `gorm:"primaryKey;autoIncrement" json:"id"` - AgentId string `json:"agentId"` // 探针ID - MonitorId string `json:"monitorId"` // 监控项ID - Type string `json:"type"` // 监控类型: http, tcp - Target string `json:"target"` // 监控目标 - Status string `json:"status"` // 状态: up, down - StatusCode int `json:"statusCode"` // HTTP状态码 - ResponseTime int64 `json:"responseTime"` // 响应时间(毫秒) - Error string `json:"error"` // 错误信息 - Message string `json:"message"` // 附加信息 - ContentMatch bool `json:"contentMatch"` // 内容匹配结果 - CertExpiryTime int64 `json:"certExpiryTime"` // 证书过期时间(毫秒时间戳), 0表示无证书 - CertDaysLeft int `json:"certDaysLeft"` // 证书剩余天数 - Timestamp int64 `gorm:"index:idx_agent_name_time;index:idx_agent_time;index:idx_time" json:"timestamp"` // 时间戳(毫秒) + AgentId string `gorm:"index:idx_mon_agent_monitor_ts,priority:1" json:"agentId"` // 探针ID + MonitorId string `gorm:"index:idx_mon_agent_monitor_ts,priority:2;index:idx_mon_type_monitor_ts,priority:2" json:"monitorId"` // 监控项ID + Type string `gorm:"index:idx_mon_type_monitor_ts,priority:1" json:"type"` // 监控类型: http, tcp + Target string `json:"target"` // 监控目标 + Status string `json:"status"` // 状态: up, down + StatusCode int `json:"statusCode"` // HTTP状态码 + ResponseTime int64 `json:"responseTime"` // 响应时间(毫秒) + Error string `json:"error"` // 错误信息 + Message string `json:"message"` // 附加信息 + ContentMatch bool `json:"contentMatch"` // 内容匹配结果 + CertExpiryTime int64 `json:"certExpiryTime"` // 证书过期时间(毫秒时间戳), 0表示无证书 + CertDaysLeft int `json:"certDaysLeft"` // 证书剩余天数 + Timestamp int64 `gorm:"index:idx_mon_agent_monitor_ts,priority:3;index:idx_mon_type_monitor_ts,priority:3;index:idx_mon_ts" json:"timestamp"` // 时间戳(毫秒) } func (MonitorMetric) TableName() string { diff --git a/internal/repo/metric_repo.go b/internal/repo/metric_repo.go index 009bb1a..0246607 100644 --- a/internal/repo/metric_repo.go +++ b/internal/repo/metric_repo.go @@ -6,6 +6,7 @@ import ( "github.com/dushixiang/pika/internal/models" "gorm.io/gorm" + "gorm.io/gorm/clause" ) type MetricRepo struct { @@ -98,17 +99,14 @@ func (r *MetricRepo) GetLatestTemperatureMetrics(ctx context.Context, agentID st return metrics, err } -// SaveHostMetric 保存主机信息指标(只保留最新的一条记录) +// SaveHostMetric 保存主机信息指标(按 agent 覆盖,避免先删后插的空窗) func (r *MetricRepo) SaveHostMetric(ctx context.Context, metric *models.HostMetric) error { - // 使用事务确保原子性 - return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { - // 先删除该 agent 的所有旧记录 - if err := tx.Where("agent_id = ?", metric.AgentID).Delete(&models.HostMetric{}).Error; err != nil { - return err - } - // 插入新记录 - return tx.Create(metric).Error - }) + return r.db.WithContext(ctx). + Clauses(clause.OnConflict{ + Columns: []clause.Column{{Name: "agent_id"}}, + DoUpdates: clause.AssignmentColumns([]string{"os", "platform", "platform_version", "kernel_version", "kernel_arch", "uptime", "boot_time", "procs", "timestamp"}), + }). + Create(metric).Error } // GetLatestHostMetric 获取最新的主机信息 @@ -467,31 +465,20 @@ func (r *MetricRepo) GetMonitorMetrics(ctx context.Context, agentID, monitorID s // GetLatestMonitorMetrics 获取最新的监控指标(每个监控项的最新一条) func (r *MetricRepo) GetLatestMonitorMetrics(ctx context.Context, agentID string) ([]models.MonitorMetric, error) { - // 先获取该 agent 下所有监控项ID - var monitorIDs []string - err := r.db.WithContext(ctx). - Model(&models.MonitorMetric{}). - Where("agent_id = ?", agentID). - Distinct("monitor_id"). - Pluck("monitor_id", &monitorIDs).Error - if err != nil { - return nil, err - } - - // 对每个监控项获取最新的一条记录 var metrics []models.MonitorMetric - for _, monitorID := range monitorIDs { - var metric models.MonitorMetric - err := r.db.WithContext(ctx). - Where("agent_id = ? AND monitor_id = ?", agentID, monitorID). - Order("timestamp DESC"). - First(&metric).Error - if err == nil { - metrics = append(metrics, metric) - } - } - - return metrics, nil + err := r.db.WithContext(ctx).Raw(` + SELECT m.* + FROM monitor_metrics m + INNER JOIN ( + SELECT monitor_id, MAX(timestamp) AS ts + FROM monitor_metrics + WHERE agent_id = ? + GROUP BY monitor_id + ) latest ON m.monitor_id = latest.monitor_id AND m.timestamp = latest.ts + WHERE m.agent_id = ? + ORDER BY m.monitor_id + `, agentID, agentID).Scan(&metrics).Error + return metrics, err } // GetMonitorMetricsByName 获取指定监控项的历史数据 @@ -682,18 +669,6 @@ func (r *MetricRepo) DeleteMonitorMetrics(ctx context.Context, monitorID string) // DeleteAgentMetrics 删除指定探针的所有指标数据 func (r *MetricRepo) DeleteAgentMetrics(ctx context.Context, agentID string) error { - tx := r.db.WithContext(ctx).Begin() - if tx.Error != nil { - return tx.Error - } - - defer func() { - if r := recover(); r != nil { - tx.Rollback() - } - }() - - // 删除各类指标数据 tables := []interface{}{ &models.CPUMetric{}, &models.MemoryMetric{}, @@ -707,71 +682,50 @@ func (r *MetricRepo) DeleteAgentMetrics(ctx context.Context, agentID string) err &models.MonitorMetric{}, } - for _, table := range tables { - if err := tx.Where("agent_id = ?", agentID).Delete(table).Error; err != nil { - tx.Rollback() - return err + return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + for _, table := range tables { + if err := tx.Where("agent_id = ?", agentID).Delete(table).Error; err != nil { + return err + } } - } - - return tx.Commit().Error + return nil + }) } // GetLatestMonitorMetricsByType 获取指定类型的最新监控指标(所有探针) func (r *MetricRepo) GetLatestMonitorMetricsByType(ctx context.Context, monitorType string) ([]*models.MonitorMetric, error) { var metrics []*models.MonitorMetric - // 获取所有符合类型的监控项 - var monitorIDs []string - err := r.db.WithContext(ctx). - Model(&models.MonitorMetric{}). - Where("type = ?", monitorType). - Distinct("monitor_id"). - Pluck("monitor_id", &monitorIDs).Error - if err != nil { - return nil, err - } + err := r.db.WithContext(ctx).Raw(` + SELECT m.* + FROM monitor_metrics m + INNER JOIN ( + SELECT monitor_id, MAX(timestamp) AS ts + FROM monitor_metrics + WHERE type = ? + GROUP BY monitor_id + ) latest ON m.monitor_id = latest.monitor_id AND m.timestamp = latest.ts + WHERE m.type = ? + ORDER BY m.monitor_id + `, monitorType, monitorType).Scan(&metrics).Error - // 对每个监控项获取最新的一条记录 - for _, monitorID := range monitorIDs { - var metric models.MonitorMetric - err := r.db.WithContext(ctx). - Where("monitor_id = ? AND type = ?", monitorID, monitorType). - Order("timestamp DESC"). - First(&metric).Error - if err == nil { - metrics = append(metrics, &metric) - } - } - - return metrics, nil + return metrics, err } // GetAllLatestMonitorMetrics 获取所有最新的监控指标(所有探针的所有监控项,每个监控项的最新一条) func (r *MetricRepo) GetAllLatestMonitorMetrics(ctx context.Context) ([]*models.MonitorMetric, error) { var metrics []*models.MonitorMetric - // 获取所有监控项ID - var monitorIDs []string - err := r.db.WithContext(ctx). - Model(&models.MonitorMetric{}). - Distinct("monitor_id"). - Pluck("monitor_id", &monitorIDs).Error - if err != nil { - return nil, err - } + err := r.db.WithContext(ctx).Raw(` + SELECT m.* + FROM monitor_metrics m + INNER JOIN ( + SELECT monitor_id, MAX(timestamp) AS ts + FROM monitor_metrics + GROUP BY monitor_id + ) latest ON m.monitor_id = latest.monitor_id AND m.timestamp = latest.ts + ORDER BY m.monitor_id + `).Scan(&metrics).Error - // 对每个监控项获取最新的一条记录 - for _, monitorID := range monitorIDs { - var metric models.MonitorMetric - err := r.db.WithContext(ctx). - Where("monitor_id = ?", monitorID). - Order("timestamp DESC"). - First(&metric).Error - if err == nil { - metrics = append(metrics, &metric) - } - } - - return metrics, nil + return metrics, err }