# v1.2.0 - 性能优化与缓存策略

## 多级缓存架构

```
┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│   L1 Cache  │ ──→ │   L2 Cache  │ ──→ │   Database  │
│  (Local)    │     │  (Redis)    │     │  (MySQL)    │
│   10ms      │     │   1-5ms     │     │   10-50ms   │
└─────────────┘     └─────────────┘     └─────────────┘
```

### 本地缓存 (L1)

```go
package cache

import (
    "time"
    "github.com/patrickmn/go-cache"
)

type LocalCache struct {
    c *cache.Cache
}

func NewLocalCache(defaultExpiration, cleanupInterval time.Duration) *LocalCache {
    return &LocalCache{
        c: cache.New(defaultExpiration, cleanupInterval),
    }
}

func (lc *LocalCache) Get(key string) (interface{}, bool) {
    return lc.c.Get(key)
}

func (lc *LocalCache) Set(key string, value interface{}, duration time.Duration) {
    lc.c.Set(key, value, duration)
}

func (lc *LocalCache) Delete(key string) {
    lc.c.Delete(key)
}

// 使用示例
var userCache = NewLocalCache(5*time.Minute, 10*time.Minute)

func (c *Core) GetUserWithLocalCache(ctx context.Context, userID int64) (*User, error) {
    key := fmt.Sprintf("user:%d", userID)
    
    // L1缓存
    if val, found := userCache.Get(key); found {
        return val.(*User), nil
    }
    
    // L2缓存 (Redis)
    user, err := c.getUserFromRedis(ctx, userID)
    if err == nil {
        userCache.Set(key, user, 5*time.Minute)
        return user, nil
    }
    
    // 数据库
    user, err = c.dao.GetUser(ctx, userID)
    if err != nil {
        return nil, err
    }
    
    // 回填缓存
    c.setUserToRedis(ctx, user)
    userCache.Set(key, user, 5*time.Minute)
    
    return user, nil
}
```

### Redis缓存模式

```go
package cache

import (
    "context"
    "encoding/json"
    "time"
    "github.com/redis/go-redis/v9"
)

type RedisCache struct {
    client *redis.Client
}

// CacheAside 旁路缓存模式
func (rc *RedisCache) CacheAside(ctx context.Context, key string, 
    expiration time.Duration, fn func() (interface{}, error)) (interface{}, error) {
    
    // 1. 读缓存
    val, err := rc.client.Get(ctx, key).Result()
    if err == nil {
        return val, nil
    }
    
    // 2. 缓存未命中，读数据库
    data, err := fn()
    if err != nil {
        return nil, err
    }
    
    // 3. 回填缓存
    bytes, _ := json.Marshal(data)
    rc.client.Set(ctx, key, bytes, expiration)
    
    return data, nil
}

// CacheAsideWithLock 防止缓存击穿
func (rc *RedisCache) CacheAsideWithLock(ctx context.Context, key string,
    expiration time.Duration, lockTimeout time.Duration,
    fn func() (interface{}, error)) (interface{}, error) {
    
    // 1. 读缓存
    val, err := rc.client.Get(ctx, key).Result()
    if err == nil {
        return val, nil
    }
    
    // 2. 尝试获取分布式锁
    lockKey := key + ":lock"
    locked, err := rc.client.SetNX(ctx, lockKey, "1", lockTimeout).Result()
    if err != nil || !locked {
        // 获取锁失败，等待后重试
        time.Sleep(100 * time.Millisecond)
        return rc.client.Get(ctx, key).Result()
    }
    
    // 3. 释放锁
    defer rc.client.Del(ctx, lockKey)
    
    // 4. 再次检查缓存（双重检查）
    val, err = rc.client.Get(ctx, key).Result()
    if err == nil {
        return val, nil
    }
    
    // 5. 读数据库
    data, err := fn()
    if err != nil {
        return nil, err
    }
    
    // 6. 回填缓存
    bytes, _ := json.Marshal(data)
    rc.client.Set(ctx, key, bytes, expiration)
    
    return data, nil
}

// WriteThrough 直写模式
func (rc *RedisCache) WriteThrough(ctx context.Context, key string,
    data interface{}, expiration time.Duration,
    writeFn func() error) error {
    
    // 1. 先写数据库
    if err := writeFn(); err != nil {
        return err
    }
    
    // 2. 再写缓存
    bytes, _ := json.Marshal(data)
    return rc.client.Set(ctx, key, bytes, expiration).Err()
}

// WriteBehind 异步写（高吞吐场景）
func (rc *RedisCache) WriteBehind(ctx context.Context, key string,
    data interface{}, expiration time.Duration,
    writeFn func() error) error {
    
    // 1. 先写缓存
    bytes, _ := json.Marshal(data)
    if err := rc.client.Set(ctx, key, bytes, expiration).Err(); err != nil {
        return err
    }
    
    // 2. 异步写数据库
    go func() {
        if err := writeFn(); err != nil {
            // 失败处理：重试或记录日志
        }
    }()
    
    return nil
}

// Invalidate 缓存失效
func (rc *RedisCache) Invalidate(ctx context.Context, keys ...string) error {
    return rc.client.Del(ctx, keys...).Err()
}

// InvalidatePattern 批量失效
func (rc *RedisCache) InvalidatePattern(ctx context.Context, pattern string) error {
    keys, err := rc.client.Keys(ctx, pattern).Result()
    if err != nil {
        return err
    }
    if len(keys) > 0 {
        return rc.client.Del(ctx, keys...).Err()
    }
    return nil
}
```

## 缓存一致性策略

### Cache-Aside + 延迟双删

```go
func (c *Core) UpdateUser(ctx context.Context, user *User) error {
    key := fmt.Sprintf("user:%d", user.ID)
    
    // 1. 删除缓存
    c.redis.Del(ctx, key)
    
    // 2. 更新数据库
    if err := c.dao.UpdateUser(ctx, user); err != nil {
        return err
    }
    
    // 3. 延迟再次删除（防止脏读）
    go func() {
        time.Sleep(500 * time.Millisecond)
        c.redis.Del(ctx, key)
    }()
    
    return nil
}
```

### 基于Binlog的缓存同步

```go
// Canal监听MySQL binlog
func (cs *CacheSync) HandleBinlog(event *canal.RowsEvent) {
    switch event.Table.Name {
    case "users":
        for _, row := range event.Rows {
            userID := row[0].(int64)
            
            // 删除缓存
            cs.redis.Del(ctx, fmt.Sprintf("user:%d", userID))
            
            // 删除本地缓存
            userCache.Delete(fmt.Sprintf("user:%d", userID))
        }
    }
}
```

## 数据库查询优化

### 分页查询优化

```go
// 游标分页（推荐）
func (d *DAO) GetMessagesCursor(ctx context.Context, chatID int64, cursor int64, limit int) ([]*Message, error) {
    query := `
        SELECT * FROM messages 
        WHERE chat_id = ? AND id < ? 
        ORDER BY id DESC 
        LIMIT ?
    `
    var msgs []*Message
    err := d.db.QueryRowsCtx(ctx, &msgs, query, chatID, cursor, limit)
    return msgs, err
}

// 避免OFFSET大数值的性能问题
// 差：OFFSET 100000
// 好：WHERE id < last_seen_id LIMIT 20
```

### 批量操作

```go
// 批量插入
func (d *DAO) BatchInsertMessages(ctx context.Context, msgs []*Message) error {
    query := `INSERT INTO messages (chat_id, sender_id, content, created_at) VALUES `
    
    var values []string
    var args []interface{}
    
    for _, msg := range msgs {
        values = append(values, "(?, ?, ?, ?)")
        args = append(args, msg.ChatID, msg.SenderID, msg.Content, msg.CreatedAt)
    }
    
    query += strings.Join(values, ", ")
    _, err := d.db.ExecCtx(ctx, query, args...)
    return err
}

// 批量查询（IN查询优化）
func (d *DAO) GetUsersByIDs(ctx context.Context, userIDs []int64) ([]*User, error) {
    // 分批处理，每批1000
    batchSize := 1000
    var result []*User
    
    for i := 0; i < len(userIDs); i += batchSize {
        end := i + batchSize
        if end > len(userIDs) {
            end = len(userIDs)
        }
        
        batch := userIDs[i:end]
        query, args, _ := sqlx.In(`SELECT * FROM users WHERE id IN (?)`, batch)
        query = d.db.Rebind(query)
        
        var users []*User
        err := d.db.SelectCtx(ctx, &users, query, args...)
        if err != nil {
            return nil, err
        }
        
        result = append(result, users...)
    }
    
    return result, nil
}
```

## 连接池优化

```go
// MySQL连接池配置
db, err := sqlx.Open("mysql", dataSource)
if err != nil {
    log.Fatal(err)
}

// 连接池设置
db.SetMaxOpenConns(100)        // 最大连接数
db.SetMaxIdleConns(20)         // 空闲连接数
db.SetConnMaxLifetime(5 * time.Minute)  // 连接最大生命周期
db.SetConnMaxIdleTime(1 * time.Minute)  // 空闲连接最大时间

// Redis连接池配置
redisClient := redis.NewClient(&redis.Options{
    Addr:         "localhost:6379",
    PoolSize:     100,              // 连接池大小
    MinIdleConns: 10,               // 最小空闲连接
    MaxConnAge:   5 * time.Minute,  // 连接最大年龄
})
```

## 性能监控

```go
// 慢查询监控
func SlowQueryMiddleware(next HandlerFunc) HandlerFunc {
    return func(ctx context.Context, req Request) (Response, error) {
        start := time.Now()
        resp, err := next(ctx, req)
        duration := time.Since(start)
        
        if duration > 100*time.Millisecond {
            log.Warn().
                Str("method", req.Method).
                Dur("duration", duration).
                Msg("slow query detected")
        }
        
        return resp, err
    }
}

// 缓存命中率监控
type CacheStats struct {
    Hits   int64
    Misses int64
}

func (cs *CacheStats) HitRate() float64 {
    total := cs.Hits + cs.Misses
    if total == 0 {
        return 0
    }
    return float64(cs.Hits) / float64(total)
}
```
