Skip to content

缓存设计 ​

缓存策略 ​

Cache Aside(旁路缓存) ​

最常用的模式,应用程序同时维护缓存和数据库。

go
// 读操作
func Get(key string) string {
    // 1. 查缓存
    val := rdb.Get(ctx, key).Val()
    if val != "" {
        return val
    }
    
    // 2. 查数据库
    val = db.Query(key)
    
    // 3. 写缓存
    rdb.Set(ctx, key, val, 1*time.Hour)
    return val
}

// 写操作
func Set(key, value string) {
    // 1. 更新数据库
    db.Update(key, value)
    
    // 2. 删除缓存
    rdb.Del(ctx, key)
}

Write Through(写穿透) ​

应用程序只写缓存,由缓存负责写数据库。

Write Behind(写回) ​

应用程序只写缓存,异步批量写入数据库。

缓存问题 ​

缓存穿透 ​

查询不存在的数据,缓存和数据库都无数据。

解决方案:

  1. 缓存空值
go
val := rdb.Get(ctx, key).Val()
if val == "NULL" {
    return ""  // 缓存的空值
}
  1. 布隆过滤器
go
// 使用布隆过滤器预先判断
if !bloomFilter.Test(key) {
    return ""  // key 不存在
}

缓存击穿 ​

热点 key 过期,大量请求直达数据库。

解决方案:

  1. 热点数据永不过期
  2. 互斥锁
go
if rdb.SetNX(ctx, "lock:"+key, 1, 10*time.Second).Val() {
    defer rdb.Del(ctx, "lock:"+key)
    // 查数据库并更新缓存
}

缓存雪崩 ​

大量 key 同时过期。

解决方案:

  1. 随机过期时间
go
expire := 3600 + rand.Intn(600)  // 1小时 ± 10分钟
rdb.Set(ctx, key, value, time.Duration(expire)*time.Second)
  1. 高可用架构:哨兵/集群

最佳实践 ​

  1. 键命名:业务:模块:id
  2. 过期时间:随机化避免雪崩
  3. 序列化:JSON/Protobuf
  4. 监控:命中率、延迟、内存

在下一章中,我们将学习分布式锁。