数据库缓存策略详解1. 缓存架构概述缓存是提升数据库性能的重要手段通过将热点数据存储在内存中减少数据库访问压力。2. 多级缓存2.1 本地缓存import github.com/patrickmn/go-cache var localCache cache.New(5*time.Minute, 10*time.Minute) // 设置缓存 localCache.Set(user:1, userData, cache.DefaultExpiration) // 获取缓存 if value, found : localCache.Get(user:1); found { user : value.(*User) return user, nil }2.2 分布式缓存import github.com/redis/go-redis/v9 rdb : redis.NewClient(redis.Options{ Addr: localhost:6379, }) // 设置缓存带过期时间 err : rdb.Set(ctx, user:1, data, 30*time.Minute).Err() // 获取缓存 data, err : rdb.Get(ctx, user:1).Bytes()3. Cache-Aside模式3.1 读操作func GetUser(ctx context.Context, userID int) (*User, error) { cacheKey : fmt.Sprintf(user:%d, userID) // 1. 先查本地缓存 if localData, found : localCache.Get(cacheKey); found { return localData.(*User), nil } // 2. 再查Redis if redisData, err : rdb.Get(ctx, cacheKey).Bytes(); err nil { var user User json.Unmarshal(redisData, user) localCache.Set(cacheKey, user, 5*time.Minute) return user, nil } // 3. 最后查数据库 var user User if err : db.First(user, userID).Error; err ! nil { return nil, err } // 4. 回填缓存 data, _ : json.Marshal(user) rdb.Set(ctx, cacheKey, data, 30*time.Minute) localCache.Set(cacheKey, user, 5*time.Minute) return user, nil }3.2 写操作func UpdateUser(ctx context.Context, user *User) error { // 1. 更新数据库 if err : db.Save(user).Error; err ! nil { return err } cacheKey : fmt.Sprintf(user:%d, user.ID) // 2. 删除缓存不是更新 localCache.Delete(cacheKey) rdb.Del(ctx, cacheKey) return nil }4. 缓存过期策略4.1 TTL策略// 设置不同过期时间 type CacheConfig struct { UserCacheTTL time.Duration // 30分钟 ProductCacheTTL time.Duration // 5分钟 ConfigCacheTTL time.Duration // 24小时 } // 热点数据设置较长的TTL rdb.Set(ctx, hot:product:1, data, 5*time.Minute) // 冷门数据设置较短的TTL rdb.Set(ctx, cold:product:99999, data, 30*time.Second)4.2 LRU策略import github.com/hashicorp/golang-lru lruCache, _ : lru.NewARC(10000) // 最多缓存10000条 // 使用LRU缓存 if value, ok : lruCache.Get(key); ok { return value.([]byte) } lruCache.Add(key, value)5. 缓存问题5.1 缓存穿透func GetUserWithProtection(ctx context.Context, userID int) (*User, error) { cacheKey : fmt.Sprintf(user:%d, userID) //布隆过滤器检查 if !bloomFilter.Exists(cacheKey) { return nil, ErrUserNotFound } // 正常的缓存查询 return getUserFromCache(ctx, userID) } // 布隆过滤器实现 type BloomFilter struct { bitmap []byte size uint } func (bf *BloomFilter) Add(key string) { for i : uint(0); i 3; i { pos : hash(key, i) % bf.size bf.bitmap[pos/8] | 1 (pos % 8) } } func (bf *BloomFilter) Exists(key string) bool { for i : uint(0); i 3; i { pos : hash(key, i) % bf.size if bf.bitmap[pos/8](1(pos%8)) 0 { return false } } return true }5.2 缓存击穿import sync type SingleFlight struct { mu sync.Mutex m map[string]*call } type call struct { wg sync.WaitGroup val interface{} err error } func (sf *SingleFlight) Do(key string, fn func() (interface{}, error)) (interface{}, error) { sf.mu.Lock() if sf.m nil { sf.m make(map[string]*call) } if c, ok : sf.m[key]; ok { sf.mu.Unlock() c.wg.Wait() return c.val, c.err } c : new(call) c.wg.Add(1) sf.m[key] c sf.mu.Unlock() c.val, c.err fn() c.wg.Done() sf.mu.Lock() delete(sf.m, key) sf.mu.Unlock() return c.val, c.err } // 使用SingleFlight user, err : sf.Do(fmt.Sprintf(user:%d, userID), func() (interface{}, error) { return getUserFromDB(userID) })5.3 缓存雪崩// 方案1随机过期时间 func GetUser(ctx context.Context, userID int) (*User, error) { cacheKey : fmt.Sprintf(user:%d, userID) // 添加随机过期时间避免同时过期 jitter : time.Duration(rand.Int63n(300)) * time.Second data, err : rdb.Get(ctx, cacheKey).Bytes() if err nil { var user User json.Unmarshal(data, user) return user, nil } user, err : getUserFromDB(userID) if err ! nil { return nil, err } data, _ json.Marshal(user) rdb.Set(ctx, cacheKey, data, 30*time.Minutejitter) return user, nil } // 方案2二级缓存 type TwoLevelCache struct { l1 *cache.Cache l2 *redis.Client } func (c *TwoLevelCache) Get(ctx context.Context, key string) ([]byte, error) { // L1缓存 if val, found : c.l1.Get(key); found { return val.([]byte), nil } // L2缓存 data, err : c.l2.Get(ctx, key).Bytes() if err nil { c.l1.Set(key, data, 5*time.Minute) return data, nil } return nil, err }6. 缓存一致性6.1 双删策略func UpdateUser(ctx context.Context, user *User) error { cacheKey : fmt.Sprintf(user:%d, user.ID) // 1. 先删除缓存 rdb.Del(ctx, cacheKey) // 2. 更新数据库 if err : db.Save(user).Error; err ! nil { return err } // 3. 延迟再删除缓存 time.Sleep(100 * time.Millisecond) rdb.Del(ctx, cacheKey) return nil }6.2 Binlog同步// 使用Canal监听数据库变更 type BinlogSync struct { canal *canal.Client rdb *redis.Client } func (s *BinlogSync) Start() error { ddl, rows : s.canal.GetRows(100) for _, row : range rows { if row.EventType canal.Update { table : row.Table.Name data : row.NewRow.Map if table users { userID : data[id] cacheKey : fmt.Sprintf(user:%v, userID) s.rdb.Del(ctx, cacheKey) } } } return nil }7. 总结缓存是提升系统性能的重要手段需要根据业务场景选择合适的缓存策略。Cache-Aside模式是最常用的缓存读写模式同时需要注意缓存穿透、击穿、雪崩等问题通过布隆过滤器、SingleFlight、随机过期时间等手段进行防护。