Files
aiops/docs/redis-caching-strategy.md
T
attacker 65636b3f5a fix: favicon .ico 生成 + README 更新 + run.sh kubernetes 别名
- 新增 frontend/public/favicon.ico (4 size: 16/32/48/64)
- index.html .ico 引用调整到 .svg 前面(兼容旧浏览器)
- README.md 全量更新:Kubernetes 完整命令列表 + K8s 管理亮点
- run.sh 主入口添加 k8s 别名(向后兼容)
- aiops-development skill 更新部署命令和 favicon 文档
2026-06-25 00:53:57 +08:00

184 lines
5.7 KiB
Markdown
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.
# Redis 缓存策略文档
> 版本: v0.43 | 最后更新: 2026-06-24
## 缓存总览
| 模块 | 缓存 | 方式 | Key 格式 | TTL | 说明 |
|------|:--:|------|----------|-----|------|
| Agent 指标热数据 | ✅ | Sorted Set | `agent:metrics:{hostname}:{metric}` | 7 天 | 聚合后批量写 MySQL |
| 流水线日志 | ✅ | pub/sub + String | `pipeline:log:{runId}` | 30 min | 实时推送 + 历史缓存 |
| Dashboard 统计 | ✅ | String (JSON) | `dashboard:stats` | 30 s | ~25 次 SQL → 1 次 Redis Get |
| 服务器列表 | ✅ | String (JSON) | `servers:list:{kw}:{st}:{reg}:{p}:{ps}` | 10 s | JOIN 查询 → 缓存命中 ~1ms |
| 告警中心 | ❌ | — | — | — | 表小,直接查 MySQL |
| K8s 资源 | ❌ | — | — | — | 走 K8s API,不经过 MySQL |
## 缓存后端
支持双后端,通过配置 `driver` 字段切换:
| Driver | 场景 | 依赖 |
|--------|------|------|
| `redis` | Docker / 生产环境 | 外部 Redis 7 容器 |
| `redka` | 二进制独立部署 | 内嵌,零依赖 |
```yaml
# config.yaml
redis:
driver: redis # redis | redka
host: aiops-redis # Docker 容器名
port: 6379
password: ""
db: 0
```
初始化逻辑在 `backend/cmd/main.go:129`,如果 Redis 不可用则 `cache = nil`,业务自动回退到无缓存模式。
## 缓存接口
```go
// pkg/redis/redis.go
type Cache interface {
Get(ctx, key) (string, error)
Set(ctx, key, value, expiration) error
Del(ctx, keys...) error
// ... ZAdd / ZRangeByScore / Publish 等
}
```
## 各缓存详解
### 1. Dashboard 统计 — `dashboard:stats`
**缓存位置**: `handlers/dashboard.go:GetStats`
**变更频率**: 低(统计数据,30s 延迟可接受)
**Key**: 全局单键 `dashboard:stats`
**实现流程**:
```
请求 → cache.Get("dashboard:stats")
├─ hit → json.Unmarshal → 返回(跳过 ~25 次 SQL)
└─ miss → MySQL 聚合查询 → json.Marshal → cache.Set(30s) → 返回
```
**写入不是异步的**:`cache.Set` 使用 `context.Background()`,缓存写失败不影响 HTTP 响应。
```go
// 读缓存
if cached, err := h.cache.Get(c.Request.Context(), cacheKey); err == nil && cached != "" {
var stats models.DashboardStats
if json.Unmarshal([]byte(cached), &stats) == nil {
c.JSON(200, Response{Data: stats})
return
}
}
// ... 查询逻辑 ...
// 写缓存
if data, err := json.Marshal(stats); err == nil {
_ = h.cache.Set(context.Background(), cacheKey, string(data), 30*time.Second)
}
```
### 2. 服务器列表 — `servers:list:*`
**缓存位置**: `handlers/server.go:ListServers`
**变更频率**: 低(服务器列表相对稳定,10s TTL 足够短)
**Key 格式**: `servers:list:{keyword}:{status}:{region_id}:{page}:{page_size}`
- 按查询参数区分,不同过滤条件独立缓存
- 写操作不主动失效(TTL 10s 自动过期)
**实现流程**:
```
请求 → cache.Get("servers:list:{params}")
├─ hit → json.Unmarshal → 返回(跳过 Preload JOIN)
└─ miss → MySQL 查询 → json.Marshal → cache.Set(10s) → 返回
```
```go
// 读缓存
if h.cache != nil {
if cached, err := h.cache.Get(c.Request.Context(), cacheKey); err == nil && cached != "" {
var resp models.Response
if json.Unmarshal([]byte(cached), &resp) == nil {
c.JSON(200, resp)
return
}
}
}
// ... 查询逻辑 ...
// 写缓存
if h.cache != nil {
if data, err := json.Marshal(resp); err == nil {
_ = h.cache.Set(context.Background(), cacheKey, string(data), 10*time.Second)
}
}
```
### 3. Agent 指标热数据
**缓存位置**: `handlers/agent.go`
**Key 格式**: `agent:metrics:{hostname}:{metric}` (Sorted Set)
- Agent 上报数据先写 Redis Sorted Set(速度快)
- 定时任务批量聚合后写入 MySQL(持久化)
- TTL 7 天,历史数据以 MySQL 为准
### 4. 流水线日志
**缓存位置**: `handlers/ws_pipeline_log.go` + `handlers/pipeline.go`
- **实时推送**: Redis Pub/Sub,构建输出实时流式推送到前端
- **历史缓存**: `pipeline:log:{runId}`,TTL 30 min
- 超过 30 min 的日志从 MySQL 回查
## 容错设计
| 场景 | 行为 |
|------|------|
| Redis 不可用(启动时) | `cache = nil`,日志 Warn,正常启动 |
| Redis 不可用(运行时) | `cache.Get()` 返回 error → 走 MySQL |
| 缓存值反序列化失败 | 走 MySQL(原逻辑) |
| 缓存写失败 | 静默忽略(`_ = h.cache.Set(...)`) |
**关键原则:缓存是加速层,不是依赖。任何缓存故障都不影响功能正确性。**
## 缓存大小估算
| Key | 单条大小 | 数量 | 估算总量 |
|-----|---------|------|---------|
| `dashboard:stats` | ~3 KB | 1 | ~3 KB |
| `servers:list:*` | ~5 KB/组合 | ~5-10(常用过滤组合) | ~50 KB |
| `agent:metrics:*` | ~200 B/指标 | ~15 指标 × 7 主机 | ~21 KB |
| `pipeline:log:*` | ~50 KB/运行 | ~5 并发 | ~250 KB |
| **合计** | | | **< 500 KB** |
## 性能对比
| 端点 | 缓存前 | 缓存命中 | 加速比 |
|------|--------|---------|--------|
| `GET /dashboard/stats` | ~150ms (~25 SQL) | ~1ms | ~150x |
| `GET /servers` | ~10ms (Preload JOIN) | ~1ms | ~10x |
## Handler 缓存字段一览
所有带缓存的 handler 都持有 `cache ezredis.Cache` 字段,由 `routes.go` 统一注入:
```go
// routes.go
redisCache, _ := cache.(ezredis.Cache)
dashboardHandler := handlers.NewDashboardHandler(db, redisCache)
serverHandler := handlers.NewServerHandler(db, cfg.JWT.Secret, redisCache)
pipelineHandler := handlers.NewPipelineHandler(db, redisCache)
wsLogHandler := handlers.NewWSPipelineLogHandler(db, redisCache)
agentHandler := handlers.NewAgentHandler(db, redisCache, metricsHub)
```