# T038 缓存池实现

> 审查Agent：MiMo
> 日期：2026-07-03
> 审查范围：Redis/Valkey缓存系统设计

---

## 一、缓存系统架构

### 1.1 缓存层级

```
┌─────────────────────────────────────┐
│           应用层缓存                 │
│    (Go进程内缓存, TTL短)            │
├─────────────────────────────────────┤
│           Redis/Valkey缓存          │
│    (分布式缓存, TTL中)              │
├─────────────────────────────────────┤
│           PostgreSQL数据库           │
│    (持久化存储, 查询慢)             │
└─────────────────────────────────────┘
```

### 1.2 缓存策略

| 数据类型 | 缓存位置 | TTL | 更新策略 |
|---------|---------|-----|---------|
| 角色信息 | Redis | 5分钟 | 写时失效 |
| 会话数据 | Redis | 30分钟 | 写时失效 |
| 配置数据 | Redis | 1小时 | 定时刷新 |
| 战斗数据 | Redis | 10分钟 | 写时失效 |
| 排行榜 | Redis | 1分钟 | 实时更新 |

---

## 二、缓存Key设计

### 2.1 Key命名规范

```
{模块}:{类型}:{ID}
```

### 2.2 Key列表

| 模块 | Key格式 | TTL | 说明 |
|------|---------|-----|------|
| 角色 | `char:{id}` | 5min | 角色基本信息 |
| 角色属性 | `char:{id}:stats` | 5min | 角色战斗属性 |
| 会话 | `session:{id}` | 30min | 玩家会话数据 |
| 功法 | `manual:{char_id}:{id}` | 5min | 功法实例数据 |
| 技能 | `skill:{char_id}:{id}` | 5min | 技能实例数据 |
| 货币 | `currency:{char_id}` | 5min | 货币余额 |
| 排行榜 | `rank:{type}:{tier}` | 1min | 各类排行榜 |
| 战斗 | `battle:{id}` | 10min | 战斗记录 |
| 配置 | `config:{key}` | 1hour | 配置数据 |

---

## 三、缓存操作封装

### 3.1 基础操作

```go
// 缓存操作接口
type CacheInterface interface {
    Get(key string) (string, error)
    Set(key string, value string, ttl time.Duration) error
    Del(key string) error
    Exists(key string) (bool, error)
    Incr(key string) (int64, error)
    Expire(key string, ttl time.Duration) error
}

// Redis实现
type RedisCache struct {
    client *redis.Client
}

func (c *RedisCache) Get(key string) (string, error) {
    return c.client.Get(ctx, key).Result()
}

func (c *RedisCache) Set(key string, value string, ttl time.Duration) error {
    return c.client.Set(ctx, key, value, ttl).Err()
}

func (c *RedisCache) Del(key string) error {
    return c.client.Del(ctx, key).Err()
}
```

### 3.2 业务封装

```go
// 角色缓存
func GetCharacterCache(charID string) (*Character, error) {
    key := "char:" + charID
    data, err := cache.Get(key)
    if err != nil {
        // 缓存未命中，从数据库加载
        char, err := loadCharacterFromDB(charID)
        if err != nil {
            return nil, err
        }
        // 写入缓存
        cache.Set(key, json.Marshal(char), 5*time.Minute)
        return char, nil
    }
    var char Character
    json.Unmarshal([]byte(data), &char)
    return &char, nil
}

func SetCharacterCache(charID string, char *Character) error {
    key := "char:" + charID
    data, _ := json.Marshal(char)
    return cache.Set(key, string(data), 5*time.Minute)
}

func InvalidateCharacterCache(charID string) error {
    keys := []string{
        "char:" + charID,
        "char:" + charID + ":stats",
        "currency:" + charID,
    }
    return cache.Del(keys...)
}
```

---

## 四、缓存一致性

### 4.1 写时失效策略

当数据更新时，立即失效相关缓存：

```go
func UpdateCharacter(charID string, data map[string]interface{}) error {
    // 1. 更新数据库
    err := updateDB(charID, data)
    if err != nil {
        return err
    }
    
    // 2. 失效缓存
    InvalidateCharacterCache(charID)
    
    return nil
}
```

### 4.2 缓存穿透防护

```go
// 防止缓存穿透：查询不存在的数据时，缓存空值
func GetWithProtection(key string) (string, error) {
    data, err := cache.Get(key)
    if err == redis.Nil {
        // 缓存未命中
        data, err := loadFromDB(key)
        if err != nil {
            // 数据不存在，缓存空值（短TTL）
            cache.Set(key, "", 1*time.Minute)
            return "", nil
        }
        cache.Set(key, data, 5*time.Minute)
        return data, nil
    }
    return data, err
}
```

---

## 五、缓存监控

### 5.1 监控指标

| 指标 | 说明 | 告警阈值 |
|------|------|---------|
| 命中率 | 缓存命中/总请求 | <80% |
| 内存使用 | Redis内存使用量 | >80% |
| 连接数 | Redis连接数 | >1000 |
| 延迟 | 缓存操作延迟 | >10ms |

### 5.2 监控命令

```bash
# 查看Redis状态
redis-cli info memory

# 查看命中率
redis-cli info stats | grep keyspace_hits

# 查看连接数
redis-cli info clients
```

---

## 六、缓存预热

### 6.1 启动预热

```go
func WarmupCache() {
    // 预热配置数据
    loadConfigCache()
    
    // 预热排行榜
    loadRankCache()
    
    // 预热热门角色
    loadHotCharacters()
}
```

### 6.2 定时刷新

```go
// 定时刷新配置缓存
go func() {
    ticker := time.NewTicker(1 * time.Hour)
    for range ticker.C {
        loadConfigCache()
    }
}()
```

---

## 七、缓存配置

### 7.1 Redis配置

```yaml
redis:
  addr: "localhost:6379"
  password: ""
  db: 0
  pool_size: 10
  min_idle_conns: 5
  max_retries: 3
  retry_delay: 100ms
```

### 7.2 缓存配置

```yaml
cache:
  character_ttl: 5m
  session_ttl: 30m
  config_ttl: 1h
  battle_ttl: 10m
  rank_ttl: 1m
  max_memory: 1gb
  eviction_policy: allkeys-lru
```

---

## 八、性能优化

### 8.1 批量操作

```go
// 批量获取
func GetMulti(keys []string) (map[string]string, error) {
    return cache.MGet(keys...)
}

// 批量设置
func SetMulti(data map[string]string, ttl time.Duration) error {
    pipe := cache.Pipeline()
    for k, v := range data {
        pipe.Set(ctx, k, v, ttl)
    }
    _, err := pipe.Exec(ctx)
    return err
}
```

### 8.2 本地缓存

```go
// 使用Go自带的sync.Map作为L1缓存
var localCache sync.Map

func GetWithLocalCache(key string) (string, error) {
    // L1: 本地缓存
    if val, ok := localCache.Load(key); ok {
        return val.(string), nil
    }
    
    // L2: Redis缓存
    data, err := cache.Get(key)
    if err != nil {
        return "", err
    }
    
    // 写入L1缓存（短TTL）
    localCache.Store(key, data)
    go func() {
        time.Sleep(30 * time.Second)
        localCache.Delete(key)
    }()
    
    return data, nil
}
```

---

*缓存池实现 v1.0 | 2026-07-03 | T038*
