Redis 缓存
Redis 缓存
Section titled “Redis 缓存”go-zero 的 cache 包对 Redis 进行了封装,支持旁路读取、写穿、缓存失效等模式,并通过 singleflight 内置防击穿保护。goctl 生成的所有带缓存的 model 均基于此包。
Redis: Host: 127.0.0.1:6379 Type: node # node=单机,cluster=集群 Pass: ""多节点(权重分片):
CacheRedis: - Host: redis-1:6379 Type: node Weight: 50 - Host: redis-2:6379 Type: node Weight: 50cacheConf := cache.CacheConf{ {RedisConf: c.Redis, Weight: 100},}// goctl 生成的 model 会自动接入,无需手动初始化conn := sqlx.NewMysql(c.DataSource)userModel := model.NewUserModel(conn, cacheConf)旁路读取(Take)
Section titled “旁路读取(Take)”Take 是核心访问模式。cache miss 时由 singleflight 保证 loader 只执行一次,并将结果序列化为 JSON 存入 Redis:
var user model.Usererr := userModel.FindOne(ctx, userId) // goctl model 内部调用 Take手动使用:
stats := cache.NewStat("user")c := cache.New(cacheConf, nil, stats, model.ErrNotFound)
var user Usererr = c.TakeCtx(ctx, &user, fmt.Sprintf("user:%d", id), func(v any) error { row, err := db.FindOne(ctx, id) if err != nil { return err } *v.(*User) = *row return nil})写后删除(Cache-Aside)
Section titled “写后删除(Cache-Aside)”goctl 生成的 Update / Delete 方法采用删除缓存而非更新缓存的模式,避免并发写入时的一致性问题:
// goctl 生成模式:更新 DB 后删除缓存,下次读取时重建func (m *defaultUserModel) Update(ctx context.Context, data *User) error { _, err := m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (sql.Result, error) { return conn.ExecCtx(ctx, updateSql, data.Username, data.Id) }, m.formatPrimary(data.Id)) return err}// 同时删除主键缓存和唯一索引缓存err = c.DelCtx(ctx, fmt.Sprintf("user:%d", id), fmt.Sprintf("user:name:%s", username),)TTL 抖动
Section titled “TTL 抖动”go-zero 对每个缓存条目的 TTL 增加 ±10% 的随机抖动,防止大量同类型缓存在同一时刻集中过期导致数据库流量突增。
- 写入后删除缓存而不是更新缓存,避免并发竞态。
- 短 TTL + 空值缓存结合,兼顾数据新鲜度与缓存穿透防护。
- 监控命中率,低于 90% 时通常意味着 TTL 过短或 key 空间过大。
- 高频写入(每秒多次更新)的字段不适合缓存,频繁失效的开销会超过收益。