# Go-Zero 框架 Redis 配置完全指南 **框架版本:** go-zero v1.5+ **Redis 版本:** 7.0.12 **部署环境:** Kubernetes (juwan namespace) **文档日期:** 2026年2月22日 --- ## 📋 目录 1. [配置概览](#配置概览) 2. [单节点模式](#单节点模式) 3. [Sentinel 哨兵模式](#sentinel-哨兵模式) 4. [集群模式](#集群模式) 5. [配置项详解](#配置项详解) 6. [代码实现](#代码实现) 7. [常用操作示例](#常用操作示例) 8. [高级特性](#高级特性) 9. [性能优化](#性能优化) 10. [故障排查](#故障排查) 11. [最佳实践](#最佳实践) --- ## 🎯 配置概览 ### Go-Zero Redis 支持的模式 | 模式 | Type 值 | 用途 | 高可用 | 推荐度 | |-----|---------|------|--------|--------| | **单节点** | `node` | 开发/测试 | ❌ | ⭐⭐ | | **Sentinel** | `sentinel` | 生产环境 | ✅ | ⭐⭐⭐⭐⭐ | | **集群** | `cluster` | 大规模分片 | ✅ | ⭐⭐⭐⭐ | ### 配置文件位置 ``` app/users/rpc/ ├── etc/ │ └── pb.yaml ← Redis 配置写在这里 ├── internal/ │ ├── config/ │ │ └── config.go ← 定义配置结构 │ └── svc/ │ └── serviceContext.go ← 初始化 Redis 客户端 └── pb.go ``` --- ## 🔵 单节点模式 ### 适用场景 - ✅ 开发环境 - ✅ 测试环境 - ✅ POC 演示 - ❌ 生产环境(无高可用) ### 配置文件 **`app/users/rpc/etc/pb.yaml`** ```yaml Name: user.rpc ListenOn: 0.0.0.0:9001 # Redis 单节点配置 Redis: Host: user-redis-master.juwan.svc.cluster.local:6379 Type: node Pass: ${REDIS_PASSWORD} # 从环境变量读取 # Db: 0 # 可选,默认 0 # MaxIdle: 8 # 可选,连接池最大闲置连接数 # MaxActive: 0 # 可选,连接池最大活跃连接数,0 表示无限制 Etcd: Hosts: - etcd-service.juwan.svc.cluster.local:2379 Key: user.rpc ``` ### Config 结构 **`app/users/rpc/internal/config/config.go`** ```go package config import ( "github.com/zeromicro/go-zero/core/stores/redis" "github.com/zeromicro/go-zero/zrpc" ) type Config struct { zrpc.RpcServerConf Redis redis.RedisConf // Redis 配置 } ``` ### ServiceContext 初始化 **`app/users/rpc/internal/svc/serviceContext.go`** ```go package svc import ( "github.com/zeromicro/go-zero/core/stores/redis" "juwan-backend/app/users/rpc/internal/config" ) type ServiceContext struct { Config config.Config Redis *redis.Redis } func NewServiceContext(c config.Config) *ServiceContext { return &ServiceContext{ Config: c, Redis: redis.MustNewRedis(c.Redis), // 初始化 Redis } } ``` ### 使用示例 **`app/users/rpc/internal/logic/getUsersByIdLogic.go`** ```go package logic import ( "context" "encoding/json" "fmt" "time" "juwan-backend/app/users/rpc/internal/svc" "juwan-backend/app/users/rpc/pb" "github.com/zeromicro/go-zero/core/logx" ) type GetUsersByIdLogic struct { ctx context.Context svcCtx *svc.ServiceContext logx.Logger } func NewGetUsersByIdLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetUsersByIdLogic { return &GetUsersByIdLogic{ ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx), } } func (l *GetUsersByIdLogic) GetUsersById(in *pb.GetUsersByIdReq) (*pb.GetUsersByIdResp, error) { // 缓存 key cacheKey := fmt.Sprintf("user:%d", in.Id) // 1. 尝试从缓存获取 cached, err := l.svcCtx.Redis.Get(cacheKey) if err == nil && cached != "" { // 缓存命中 var user pb.User if err := json.Unmarshal([]byte(cached), &user); err == nil { l.Logger.Infof("Cache hit for user:%d", in.Id) return &pb.GetUsersByIdResp{User: &user}, nil } } // 2. 缓存未命中,从数据库查询 l.Logger.Infof("Cache miss for user:%d, querying DB", in.Id) user := l.fetchUserFromDB(in.Id) if user == nil { return nil, fmt.Errorf("user not found") } // 3. 写入缓存(1小时过期) userJSON, _ := json.Marshal(user) err = l.svcCtx.Redis.Setex(cacheKey, string(userJSON), 3600) if err != nil { l.Logger.Errorf("Failed to set cache: %v", err) } return &pb.GetUsersByIdResp{User: user}, nil } func (l *GetUsersByIdLogic) fetchUserFromDB(id int64) *pb.User { // 实际从数据库查询 // ... return &pb.User{Id: id, Name: "John Doe"} } ``` --- ## 🟡 Sentinel 哨兵模式 ### 适用场景 - ✅✅✅ **生产环境强烈推荐** - ✅ 自动故障转移 - ✅ 高可用架构 - ✅ 主从自动切换 ### 配置文件 **`app/users/rpc/etc/pb.yaml`** ```yaml Name: user.rpc ListenOn: 0.0.0.0:9001 # Redis Sentinel 配置(推荐生产环境使用) Redis: - Host: user-redis-sentinel-sentinel.juwan.svc.cluster.local:26379 Type: sentinel Pass: ${REDIS_PASSWORD} # 或者使用完整配置 # Redis: # Host: user-redis-sentinel-sentinel.juwan.svc.cluster.local:26379 # Type: sentinel # Pass: ${REDIS_PASSWORD} # # Sentinel 特有配置 # MasterName: mymaster # Sentinel 主节点名称,默认 mymaster Etcd: Hosts: - etcd-service.juwan.svc.cluster.local:2379 Key: user.rpc ``` ### Config 结构(同单节点) **`app/users/rpc/internal/config/config.go`** ```go package config import ( "github.com/zeromicro/go-zero/core/stores/redis" "github.com/zeromicro/go-zero/zrpc" ) type Config struct { zrpc.RpcServerConf Redis redis.RedisConf // 支持所有模式 } ``` ### ServiceContext 初始化(同单节点) **`app/users/rpc/internal/svc/serviceContext.go`** ```go package svc import ( "github.com/zeromicro/go-zero/core/stores/redis" "juwan-backend/app/users/rpc/internal/config" ) type ServiceContext struct { Config config.Config Redis *redis.Redis } func NewServiceContext(c config.Config) *ServiceContext { // go-zero 会根据 Type 自动选择连接模式 return &ServiceContext{ Config: c, Redis: redis.MustNewRedis(c.Redis), } } ``` ### Sentinel 配置详解 **完整配置选项:** ```yaml Redis: Host: user-redis-sentinel-sentinel.juwan.svc.cluster.local:26379 Type: sentinel Pass: ${REDIS_PASSWORD} # Sentinel 特有配置 MasterName: mymaster # Sentinel 监控的主节点名称 # 连接池配置(可选) MaxIdle: 8 # 最大闲置连接数 MaxActive: 0 # 最大活跃连接数,0 表示无限制 IdleTimeout: 300 # 闲置连接超时时间(秒) # 超时配置(可选) ConnectTimeout: 5000 # 连接超时(毫秒) ReadTimeout: 3000 # 读超时(毫秒) WriteTimeout: 3000 # 写超时(毫秒) ``` ### 优势说明 ```go // Sentinel 模式的自动故障处理流程: // 1. 应用连接到 Sentinel app → Sentinel Service (26379) // 2. Sentinel 返回当前主节点地址 Sentinel → app: "主节点在 10.244.1.10:6379" // 3. 应用连接到主节点进行读写 app → Redis Master (10.244.1.10:6379) // 4. 主节点故障,Sentinel 检测到 Redis Master (✗ 宕机) Sentinel → 检测 → 投票 → 提升新主节点 // 5. 应用下次请求时自动连接到新主节点 app → Sentinel → "新主节点在 10.244.2.20:6379" app → New Redis Master (10.244.2.20:6379) // 整个过程应用无需重启,自动完成切换! ``` --- ## 🔴 集群模式 ### 适用场景 - ✅ 大规模数据(需要分片) - ✅ 超高并发 - ✅ 数据量超过单机内存 - ⚠️ 配置和运维复杂度高 ### 配置文件 **`app/users/rpc/etc/pb.yaml`** ```yaml Name: user.rpc ListenOn: 0.0.0.0:9001 # Redis Cluster 配置 Redis: - Host: redis-cluster-0.redis-cluster.juwan.svc.cluster.local:6379 - Host: redis-cluster-1.redis-cluster.juwan.svc.cluster.local:6379 - Host: redis-cluster-2.redis-cluster.juwan.svc.cluster.local:6379 Type: cluster Pass: ${REDIS_PASSWORD} Etcd: Hosts: - etcd-service.juwan.svc.cluster.local:2379 Key: user.rpc ``` ### Config 结构 **`app/users/rpc/internal/config/config.go`** ```go package config import ( "github.com/zeromicro/go-zero/core/stores/redis" "github.com/zeromicro/go-zero/zrpc" ) type Config struct { zrpc.RpcServerConf Redis redis.RedisConf } ``` ### 集群模式特点 **数据分片:** ``` 应用请求 ↓ 根据 key 计算 hash slot (0-16383) ↓ 路由到对应的分片节点 ↓ ┌─────────┬─────────┬─────────┐ │ Shard 1 │ Shard 2 │ Shard 3 │ │ 0-5460 │5461-10922│10923-16383│ └─────────┴─────────┴─────────┘ ``` **注意事项:** - ❌ 不支持多 key 操作(如 MGET, MSET)跨分片 - ❌ 不支持事务(MULTI/EXEC)跨分片 - ✅ 单 key 操作完全正常 - ✅ 支持 hash tag 控制 key 分布 --- ## 📚 配置项详解 ### redis.RedisConf 完整配置 **结构定义:** ```go type RedisConf struct { Host string // Redis 地址 Type string // 类型: node, sentinel, cluster Pass string // 密码 Db int // 数据库编号 (0-15),cluster 模式不支持 // Sentinel 模式专用 MasterName string // Sentinel 主节点名称 // 连接池配置 MaxIdle int // 最大闲置连接数 MaxActive int // 最大活跃连接数,0 表示无限制 IdleTimeout time.Duration // 闲置连接超时 // 超时配置 ConnectTimeout time.Duration // 连接超时 ReadTimeout time.Duration // 读超时 WriteTimeout time.Duration // 写超时 // TLS 配置(可选) Tls bool // 是否启用 TLS } ``` ### 各配置项说明 #### 1. Host(必填) **单节点模式:** ```yaml Redis: Host: user-redis-master.juwan.svc.cluster.local:6379 ``` **Sentinel 模式:** ```yaml Redis: Host: user-redis-sentinel-sentinel.juwan.svc.cluster.local:26379 # 注意:这里填 Sentinel 地址(端口 26379),不是 Redis 地址 ``` **集群模式:** ```yaml Redis: # 可以填任意一个节点,客户端会自动发现其他节点 - Host: redis-cluster-0:6379 - Host: redis-cluster-1:6379 - Host: redis-cluster-2:6379 ``` #### 2. Type(必填) | 值 | 说明 | |----|------| | `node` | 单节点模式 | | `sentinel` | Sentinel 哨兵模式 | | `cluster` | 集群模式 | #### 3. Pass(强烈推荐) **从环境变量读取(推荐):** ```yaml Redis: Pass: ${REDIS_PASSWORD} ``` **硬编码(不推荐):** ```yaml Redis: Pass: "your-password" # ❌ 不安全 ``` #### 4. Db(可选,默认 0) **适用模式:** 仅 `node` 和 `sentinel` 模式 ```yaml Redis: Db: 0 # 数据库编号 0-15 ``` **注意:** - ❌ Cluster 模式不支持多数据库 - ✅ 单节点和 Sentinel 支持 0-15 #### 5. MaxIdle(可选,默认 8) ```yaml Redis: MaxIdle: 8 # 连接池中最大闲置连接数 ``` **建议值:** - 低并发:`8` - 中并发:`16` - 高并发:`32` 或 `CPU 核心数 * 2` #### 6. MaxActive(可选,默认 0) ```yaml Redis: MaxActive: 0 # 0 表示无限制 # MaxActive: 100 # 或设置一个上限 ``` **建议值:** - 开发环境:`0`(无限制) - 生产环境:`100-500`(根据实际负载) #### 7. IdleTimeout(可选,默认 300 秒) ```yaml Redis: IdleTimeout: 300 # 秒 ``` **说明:** 闲置连接超过此时间会被关闭 #### 8. 超时配置(可选) ```yaml Redis: ConnectTimeout: 5000 # 连接超时 5 秒 ReadTimeout: 3000 # 读超时 3 秒 WriteTimeout: 3000 # 写超时 3 秒 ``` --- ## 💻 代码实现 ### 完整项目结构 ``` app/users/rpc/ ├── etc/ │ ├── pb.yaml # 开发环境配置 │ └── pb-prod.yaml # 生产环境配置 ├── internal/ │ ├── config/ │ │ └── config.go # 配置结构定义 │ ├── svc/ │ │ └── serviceContext.go # 服务上下文(初始化 Redis) │ ├── logic/ │ │ ├── addUsersLogic.go │ │ ├── getUsersByIdLogic.go │ │ └── ... │ └── server/ │ └── usercenterServer.go ├── pb/ │ ├── users.pb.go │ └── users_grpc.pb.go └── usercenter.go # 主程序入口 ``` ### 1. 配置文件示例 **开发环境 `etc/pb.yaml`:** ```yaml Name: user.rpc ListenOn: 0.0.0.0:9001 # 开发环境使用单节点 Redis: Host: localhost:6379 Type: node Pass: dev_password Db: 0 Etcd: Hosts: - localhost:2379 Key: user.rpc Log: Level: info Mode: console ``` **生产环境 `etc/pb-prod.yaml`:** ```yaml Name: user.rpc ListenOn: 0.0.0.0:9001 # 生产环境使用 Sentinel Redis: Host: user-redis-sentinel-sentinel.juwan.svc.cluster.local:26379 Type: sentinel Pass: ${REDIS_PASSWORD} MasterName: mymaster MaxIdle: 16 MaxActive: 100 IdleTimeout: 300 ConnectTimeout: 5000 ReadTimeout: 3000 WriteTimeout: 3000 Etcd: Hosts: - etcd-0.etcd.juwan.svc.cluster.local:2379 - etcd-1.etcd.juwan.svc.cluster.local:2379 - etcd-2.etcd.juwan.svc.cluster.local:2379 Key: user.rpc Log: Level: error Mode: file Path: /var/log/user-rpc KeepDays: 7 ``` ### 2. Config 定义 **`internal/config/config.go`** ```go package config import ( "github.com/zeromicro/go-zero/core/stores/redis" "github.com/zeromicro/go-zero/zrpc" ) type Config struct { zrpc.RpcServerConf // Redis 配置 Redis redis.RedisConf // 其他配置... // DB postgres.Config // Kafka kafka.Config } ``` ### 3. ServiceContext 初始化 **`internal/svc/serviceContext.go`** ```go package svc import ( "github.com/zeromicro/go-zero/core/stores/redis" "juwan-backend/app/users/rpc/internal/config" ) type ServiceContext struct { Config config.Config Redis *redis.Redis // 其他依赖... // DB *gorm.DB } func NewServiceContext(c config.Config) *ServiceContext { // 初始化 Redis(支持所有模式:node, sentinel, cluster) rdb := redis.MustNewRedis(c.Redis) return &ServiceContext{ Config: c, Redis: rdb, } } ``` ### 4. 主程序入口 **`usercenter.go`** ```go package main import ( "flag" "fmt" "juwan-backend/app/users/rpc/internal/config" "juwan-backend/app/users/rpc/internal/server" "juwan-backend/app/users/rpc/internal/svc" "juwan-backend/app/users/rpc/pb" "github.com/zeromicro/go-zero/core/conf" "github.com/zeromicro/go-zero/core/service" "github.com/zeromicro/go-zero/zrpc" "google.golang.org/grpc" "google.golang.org/grpc/reflection" ) var configFile = flag.String("f", "etc/pb.yaml", "the config file") func main() { flag.Parse() // 加载配置 var c config.Config conf.MustLoad(*configFile, &c) // 初始化服务上下文 ctx := svc.NewServiceContext(c) // 创建 gRPC 服务 s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) { pb.RegisterUsercenterServer(grpcServer, server.NewUsercenterServer(ctx)) if c.Mode == service.DevMode || c.Mode == service.TestMode { reflection.Register(grpcServer) } }) defer s.Stop() fmt.Printf("Starting rpc server at %s...\n", c.ListenOn) s.Start() } ``` --- ## 🔧 常用操作示例 ### 1. 基本读写操作 ```go package logic import ( "context" "fmt" "time" ) type UserLogic struct { ctx context.Context svcCtx *svc.ServiceContext logx.Logger } // Set 操作 func (l *UserLogic) SetUser(userId int64, data string) error { key := fmt.Sprintf("user:%d", userId) return l.svcCtx.Redis.Set(key, data) } // Setex 操作(带过期时间) func (l *UserLogic) SetUserWithExpiry(userId int64, data string) error { key := fmt.Sprintf("user:%d", userId) // 缓存 1 小时 return l.svcCtx.Redis.Setex(key, data, 3600) } // Get 操作 func (l *UserLogic) GetUser(userId int64) (string, error) { key := fmt.Sprintf("user:%d", userId) return l.svcCtx.Redis.Get(key) } // Del 操作 func (l *UserLogic) DeleteUser(userId int64) error { key := fmt.Sprintf("user:%d", userId) _, err := l.svcCtx.Redis.Del(key) return err } // Exists 检查 func (l *UserLogic) UserExists(userId int64) (bool, error) { key := fmt.Sprintf("user:%d", userId) return l.svcCtx.Redis.Exists(key) } ``` ### 2. Hash 操作 ```go // HSet 操作 func (l *UserLogic) SetUserField(userId int64, field, value string) error { key := fmt.Sprintf("user:%d", userId) return l.svcCtx.Redis.Hset(key, field, value) } // HGet 操作 func (l *UserLogic) GetUserField(userId int64, field string) (string, error) { key := fmt.Sprintf("user:%d", userId) return l.svcCtx.Redis.Hget(key, field) } // HGetAll 操作 func (l *UserLogic) GetAllUserFields(userId int64) (map[string]string, error) { key := fmt.Sprintf("user:%d", userId) return l.svcCtx.Redis.Hgetall(key) } // HMSet 批量设置 func (l *UserLogic) SetUserFields(userId int64, fields map[string]string) error { key := fmt.Sprintf("user:%d", userId) return l.svcCtx.Redis.Hmset(key, fields) } ``` ### 3. List 操作 ```go // LPush 操作 func (l *UserLogic) AddMessage(userId int64, message string) error { key := fmt.Sprintf("messages:%d", userId) _, err := l.svcCtx.Redis.Lpush(key, message) return err } // LRange 操作 func (l *UserLogic) GetMessages(userId int64, start, stop int) ([]string, error) { key := fmt.Sprintf("messages:%d", userId) return l.svcCtx.Redis.Lrange(key, start, stop) } // LLen 操作 func (l *UserLogic) GetMessageCount(userId int64) (int, error) { key := fmt.Sprintf("messages:%d", userId) return l.svcCtx.Redis.Llen(key) } ``` ### 4. Set 操作 ```go // SAdd 添加成员 func (l *UserLogic) AddUserTag(userId int64, tag string) error { key := fmt.Sprintf("user:tags:%d", userId) _, err := l.svcCtx.Redis.Sadd(key, tag) return err } // SMembers 获取所有成员 func (l *UserLogic) GetUserTags(userId int64) ([]string, error) { key := fmt.Sprintf("user:tags:%d", userId) return l.svcCtx.Redis.Smembers(key) } // SIsMember 检查成员 func (l *UserLogic) HasUserTag(userId int64, tag string) (bool, error) { key := fmt.Sprintf("user:tags:%d", userId) return l.svcCtx.Redis.Sismember(key, tag) } ``` ### 5. Sorted Set 操作 ```go // ZAdd 添加成员 func (l *UserLogic) AddToLeaderboard(userId int64, score int64) error { key := "leaderboard" _, err := l.svcCtx.Redis.Zadd(key, score, fmt.Sprintf("%d", userId)) return err } // ZRevRange 获取排行榜(从高到低) func (l *UserLogic) GetTopUsers(count int) ([]string, error) { key := "leaderboard" return l.svcCtx.Redis.Zrevrange(key, 0, int64(count-1)) } // ZRank 获取排名 func (l *UserLogic) GetUserRank(userId int64) (int64, error) { key := "leaderboard" return l.svcCtx.Redis.Zrank(key, fmt.Sprintf("%d", userId)) } ``` ### 6. 缓存模式实现 **Cache-Aside Pattern(推荐):** ```go func (l *UserLogic) GetUserById(userId int64) (*User, error) { cacheKey := fmt.Sprintf("user:%d", userId) // 1. 查缓存 cached, err := l.svcCtx.Redis.Get(cacheKey) if err == nil && cached != "" { var user User if err := json.Unmarshal([]byte(cached), &user); err == nil { return &user, nil } } // 2. 查数据库 user, err := l.getUserFromDB(userId) if err != nil { return nil, err } // 3. 写缓存 userJSON, _ := json.Marshal(user) l.svcCtx.Redis.Setex(cacheKey, string(userJSON), 3600) return user, nil } func (l *UserLogic) UpdateUser(user *User) error { // 1. 更新数据库 if err := l.updateUserInDB(user); err != nil { return err } // 2. 删除缓存(下次读取时会重新加载) cacheKey := fmt.Sprintf("user:%d", user.Id) l.svcCtx.Redis.Del(cacheKey) return nil } ``` ### 7. 分布式锁 ```go // 获取分布式锁 func (l *UserLogic) AcquireLock(key string, expiry int) (bool, error) { lockKey := fmt.Sprintf("lock:%s", key) return l.svcCtx.Redis.Setnx(lockKey, "1") } // 释放锁 func (l *UserLogic) ReleaseLock(key string) error { lockKey := fmt.Sprintf("lock:%s", key) _, err := l.svcCtx.Redis.Del(lockKey) return err } // 使用示例 func (l *UserLogic) ProcessWithLock(userId int64) error { lockKey := fmt.Sprintf("user:%d", userId) // 获取锁 acquired, err := l.AcquireLock(lockKey, 10) if err != nil { return err } if !acquired { return fmt.Errorf("failed to acquire lock") } defer l.ReleaseLock(lockKey) // 执行业务逻辑 // ... return nil } ``` ### 8. Pipeline 批量操作 ```go // 使用 go-redis 原生客户端进行 Pipeline func (l *UserLogic) BatchSetUsers(users []*User) error { // go-zero 的 Redis 包装了 go-redis,可以获取原生客户端 rdb := l.svcCtx.Redis pipe := rdb.Pipelined(func(pip redis.Pipeliner) error { for _, user := range users { key := fmt.Sprintf("user:%d", user.Id) userJSON, _ := json.Marshal(user) pip.Set(context.Background(), key, userJSON, time.Hour) } return nil }) return pipe } ``` --- ## 🚀 高级特性 ### 1. 缓存穿透防护(布隆过滤器) ```go import "github.com/zeromicro/go-zero/core/bloom" type UserLogic struct { ctx context.Context svcCtx *svc.ServiceContext filter *bloom.Filter } func (l *UserLogic) GetUserWithBloom(userId int64) (*User, error) { // 1. 布隆过滤器检查 if !l.filter.Exists([]byte(fmt.Sprintf("%d", userId))) { return nil, fmt.Errorf("user not found") } // 2. 查缓存 cacheKey := fmt.Sprintf("user:%d", userId) cached, err := l.svcCtx.Redis.Get(cacheKey) if err == nil && cached != "" { var user User json.Unmarshal([]byte(cached), &user) return &user, nil } // 3. 查数据库 user, err := l.getUserFromDB(userId) if err != nil { return nil, err } // 4. 写缓存 userJSON, _ := json.Marshal(user) l.svcCtx.Redis.Setex(cacheKey, string(userJSON), 3600) return user, nil } ``` ### 2. 缓存击穿防护(Singleflight) ```go import "golang.org/x/sync/singleflight" type UserLogic struct { ctx context.Context svcCtx *svc.ServiceContext sg singleflight.Group } func (l *UserLogic) GetUserWithSingleflight(userId int64) (*User, error) { cacheKey := fmt.Sprintf("user:%d", userId) // 使用 Singleflight 确保同一时刻只有一个请求查询 v, err, _ := l.sg.Do(cacheKey, func() (interface{}, error) { // 1. 查缓存 cached, err := l.svcCtx.Redis.Get(cacheKey) if err == nil && cached != "" { var user User json.Unmarshal([]byte(cached), &user) return &user, nil } // 2. 查数据库 user, err := l.getUserFromDB(userId) if err != nil { return nil, err } // 3. 写缓存 userJSON, _ := json.Marshal(user) l.svcCtx.Redis.Setex(cacheKey, string(userJSON), 3600) return user, nil }) if err != nil { return nil, err } return v.(*User), nil } ``` ### 3. 缓存雪崩防护(随机过期时间) ```go import ( "math/rand" "time" ) func (l *UserLogic) SetCacheWithRandomExpiry(key string, value string, baseExpiry int) error { // 在基础过期时间上增加随机值(±20%) randomOffset := rand.Intn(baseExpiry / 5) expiry := baseExpiry + randomOffset - (baseExpiry / 10) return l.svcCtx.Redis.Setex(key, value, expiry) } // 使用示例 func (l *UserLogic) CacheUser(user *User) error { key := fmt.Sprintf("user:%d", user.Id) userJSON, _ := json.Marshal(user) // 基础过期时间 1 小时,实际会在 48-72 分钟之间随机 return l.SetCacheWithRandomExpiry(key, string(userJSON), 3600) } ``` --- ## ⚡ 性能优化 ### 1. 连接池配置优化 **根据并发量调整:** ```yaml # 低并发(< 100 QPS) Redis: MaxIdle: 8 MaxActive: 100 # 中并发(100-1000 QPS) Redis: MaxIdle: 16 MaxActive: 500 # 高并发(> 1000 QPS) Redis: MaxIdle: 32 MaxActive: 1000 ``` ### 2. 超时配置优化 ```yaml Redis: # 连接超时:通常设置较大值 ConnectTimeout: 5000 # 5 秒 # 读写超时:设置较小值,快速失败 ReadTimeout: 1000 # 1 秒 WriteTimeout: 1000 # 1 秒 ``` ### 3. Pipeline 批量操作 **避免循环调用:** ```go // ❌ 不好:循环调用 for _, user := range users { key := fmt.Sprintf("user:%d", user.Id) l.svcCtx.Redis.Set(key, user.Name) } // ✅ 推荐:使用 Pipeline pipe := l.svcCtx.Redis.Pipelined(func(pip redis.Pipeliner) error { for _, user := range users { key := fmt.Sprintf("user:%d", user.Id) pip.Set(context.Background(), key, user.Name, 0) } return nil }) ``` ### 4. 合理的缓存过期时间 ```go const ( CacheExpiryShort = 300 // 5 分钟 - 热点数据 CacheExpiryMedium = 3600 // 1 小时 - 常规数据 CacheExpiryLong = 86400 // 1 天 - 冷数据 ) func (l *UserLogic) SetUserCache(user *User, expiry int) error { key := fmt.Sprintf("user:%d", user.Id) userJSON, _ := json.Marshal(user) return l.svcCtx.Redis.Setex(key, string(userJSON), expiry) } ``` ### 5. Key 命名规范 ```go // 推荐的 Key 命名规范 const ( KeyPrefixUser = "user:" // user:123 KeyPrefixSession = "session:" // session:abc123 KeyPrefixCache = "cache:" // cache:user:list KeyPrefixLock = "lock:" // lock:order:456 KeyPrefixCounter = "counter:" // counter:page:views ) // 使用函数生成 Key func UserCacheKey(userId int64) string { return fmt.Sprintf("%s%d", KeyPrefixUser, userId) } func SessionKey(sessionId string) string { return fmt.Sprintf("%s%s", KeyPrefixSession, sessionId) } ``` --- ## 🔍 故障排查 ### 1. 连接失败 **问题:** `dial tcp xxx:6379: i/o timeout` **排查步骤:** ```bash # 1. 检查 Redis 服务 kubectl get pods -n juwan | grep redis # 2. 检查 Service kubectl get svc -n juwan | grep redis # 3. 测试网络连通性 kubectl run -it --rm nettest --image=busybox --restart=Never -n juwan -- \ nc -zv user-redis-master 6379 # 4. 查看应用日志 kubectl logs -f user-rpc-xxx -n juwan ``` **解决方案:** - 确认 Host 配置正确 - 确认网络策略允许访问 - 检查 Redis Pod 状态 ### 2. 认证失败 **问题:** `NOAUTH Authentication required` **排查:** ```go // 打印配置(调试用) func main() { var c config.Config conf.MustLoad(*configFile, &c) // 检查密码是否正确加载 fmt.Printf("Redis Config: Host=%s, Pass=%s\n", c.Redis.Host, c.Redis.Pass) } ``` **解决方案:** - 确认环境变量 `REDIS_PASSWORD` 已设置 - 确认 Secret 正确挂载 - 检查密码是否正确 ### 3. 性能问题 **慢查询检测:** ```go import "time" func (l *UserLogic) GetUserWithMetrics(userId int64) (*User, error) { start := time.Now() defer func() { duration := time.Since(start) if duration > 100*time.Millisecond { l.Logger.Warnf("Slow Redis query: %v", duration) } }() // 执行查询 key := fmt.Sprintf("user:%d", userId) cached, err := l.svcCtx.Redis.Get(key) // ... } ``` **常见原因:** - 连接池耗尽 → 增大 MaxActive - 大 Value 传输 → 拆分或压缩数据 - 网络延迟 → 检查网络质量 ### 4. 内存泄漏 **检查连接是否正确关闭:** ```go // go-zero 的 Redis 客户端会自动管理连接 // 但如果使用原生 go-redis 客户端,需要手动关闭 // ❌ 错误示例 func bad() { rdb := redis.NewClient(&redis.Options{...}) // 使用完后没有关闭 } // ✅ 正确示例 func good() { rdb := redis.NewClient(&redis.Options{...}) defer rdb.Close() // ... } ``` --- ## 📖 最佳实践 ### 1. 环境变量管理 **Kubernetes Deployment:** ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: user-rpc namespace: juwan spec: template: spec: containers: - name: user-rpc image: user-rpc:v1 env: # 从 Secret 读取 Redis 密码 - name: REDIS_PASSWORD valueFrom: secretKeyRef: name: user-redis key: password # 从 ConfigMap 读取其他配置 - name: REDIS_HOST valueFrom: configMapKeyRef: name: user-rpc-config key: redis.host ``` ### 2. 配置分离 **开发、测试、生产环境分离:** ```bash # 开发环境 go run usercenter.go -f etc/pb-dev.yaml # 测试环境 go run usercenter.go -f etc/pb-test.yaml # 生产环境 ./usercenter -f etc/pb-prod.yaml ``` ### 3. 监控指标 ```go import ( "github.com/zeromicro/go-zero/core/metric" "github.com/zeromicro/go-zero/core/prometheus" ) var ( redisCacheHit = metric.NewCounterVec(&metric.CounterVecOpts{ Namespace: "user_rpc", Subsystem: "redis", Name: "cache_hit_total", Help: "redis cache hit count", Labels: []string{"key"}, }) redisCacheMiss = metric.NewCounterVec(&metric.CounterVecOpts{ Namespace: "user_rpc", Subsystem: "redis", Name: "cache_miss_total", Help: "redis cache miss count", Labels: []string{"key"}, }) ) func (l *UserLogic) GetUserWithMetrics(userId int64) (*User, error) { cacheKey := fmt.Sprintf("user:%d", userId) cached, err := l.svcCtx.Redis.Get(cacheKey) if err == nil && cached != "" { redisCacheHit.Inc("user") var user User json.Unmarshal([]byte(cached), &user) return &user, nil } redisCacheMiss.Inc("user") // 查询数据库... } ``` ### 4. 错误处理 ```go func (l *UserLogic) GetUser(userId int64) (*User, error) { cacheKey := fmt.Sprintf("user:%d", userId) // 缓存查询失败不应该中断流程 cached, err := l.svcCtx.Redis.Get(cacheKey) if err == nil && cached != "" { var user User if json.Unmarshal([]byte(cached), &user) == nil { return &user, nil } } // 缓存失败,降级查询数据库 user, err := l.getUserFromDB(userId) if err != nil { return nil, err } // 尝试回写缓存,失败不影响返回结果 go func() { userJSON, _ := json.Marshal(user) if err := l.svcCtx.Redis.Setex(cacheKey, string(userJSON), 3600); err != nil { l.Logger.Errorf("Failed to set cache: %v", err) } }() return user, nil } ``` ### 5. 缓存更新策略 **Write-Through(同步更新):** ```go func (l *UserLogic) UpdateUser(user *User) error { // 1. 更新数据库 if err := l.updateUserInDB(user); err != nil { return err } // 2. 同步更新缓存 cacheKey := fmt.Sprintf("user:%d", user.Id) userJSON, _ := json.Marshal(user) if err := l.svcCtx.Redis.Setex(cacheKey, string(userJSON), 3600); err != nil { l.Logger.Errorf("Failed to update cache: %v", err) } return nil } ``` **Write-Behind(异步更新):** ```go func (l *UserLogic) UpdateUserAsync(user *User) error { // 1. 立即更新缓存 cacheKey := fmt.Sprintf("user:%d", user.Id) userJSON, _ := json.Marshal(user) l.svcCtx.Redis.Setex(cacheKey, string(userJSON), 3600) // 2. 异步更新数据库 go func() { if err := l.updateUserInDB(user); err != nil { l.Logger.Errorf("Failed to update DB: %v", err) // 回滚缓存 l.svcCtx.Redis.Del(cacheKey) } }() return nil } ``` --- ## 📚 参考资源 ### 官方文档 - [go-zero 官方文档](https://go-zero.dev/) - [go-zero Redis 文档](https://go-zero.dev/docs/tutorials/redis) - [go-redis 文档](https://redis.uptrace.dev/) ### 示例代码 - [go-zero Examples](https://github.com/zeromicro/go-zero/tree/master/example) - [go-zero Book Store](https://github.com/zeromicro/go-zero-book-store) ### 相关工具 - [RedisInsight](https://redis.com/redis-enterprise/redis-insight/) - Redis 管理工具 - [redis-cli](https://redis.io/docs/manual/cli/) - Redis 命令行工具 --- ## 📝 总结 ### 快速开始检查清单 - [ ] 1. 在 `config.go` 中定义 `Redis redis.RedisConf` - [ ] 2. 在配置文件中添加 Redis 配置 - [ ] 3. 在 `ServiceContext` 中初始化 `redis.MustNewRedis(c.Redis)` - [ ] 4. 在 Kubernetes中配置环境变量 `REDIS_PASSWORD` - [ ] 5. 在 logic 中使用 `l.svcCtx.Redis` - [ ] 6. 测试连接是否正常 ### 生产环境推荐配置 ```yaml Redis: Host: user-redis-sentinel-sentinel.juwan.svc.cluster.local:26379 Type: sentinel Pass: ${REDIS_PASSWORD} MasterName: mymaster MaxIdle: 16 MaxActive: 100 ConnectTimeout: 5000 ReadTimeout: 3000 WriteTimeout: 3000 ``` ### 关键点提醒 1. ✅ **生产环境必须使用 Sentinel 模式** 2. ✅ **密码通过环境变量传递,不要硬编码** 3. ✅ **合理设置过期时间,防止缓存雪崩** 4. ✅ **使用 Pipeline 优化批量操作** 5. ✅ **实现缓存降级策略,Redis 故障不影响主流程** --- **文档版本:** 1.0 **创建日期:** 2026年2月22日 **维护者:** Backend Team **下次审查:** 2026年3月22日