You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Redis缓存返回部分数据问题:Go语言Postgres+Redis缓存实现异常

问题:Postgres+Redis缓存场景下GetAllUsers接口后续请求字段缺失

我实现了Postgres结合Redis缓存的GetAllUsers接口,首次请求能返回完整用户数据,但后续请求会出现部分字段缺失的情况。相关代码与User结构体定义如下:

接口实现代码

func (usr *UserImplementation) GetAllUsers(ctx context.Context) ([]models.User, error) {

    cachedUsers, err := databaseapi.Redis_CacheDB_Api()
    if err != nil {
        return nil, fmt.Errorf("error connecting to Redis cache: %v", err)
    }

    // pipe := cachedUsers.TxPipeline()

    cachedData, err := cachedUsers.Get(ctx, "users").Result()
    if err != nil && err != redis.Nil {
        return nil, fmt.Errorf("error retrieving cached data: %v", err)
    }

    if cachedData != "" {
        // Data found in the cache, return the cached data
        var cachedUsers []models.User
        err := json.Unmarshal([]byte(cachedData), &cachedUsers)
        if err != nil {
            return nil, fmt.Errorf("error unmarshaling cached data: %v", err)
        }
        return cachedUsers, nil
    }

    users, err := usr.pg.Postgres_DB_Api().DB.GetAllUsers(ctx)
    if err != nil {
        return nil, fmt.Errorf("error: %v", err.Error())
    }

    cacheData, err := json.Marshal(users)
    if err != nil {
        return nil, fmt.Errorf("error marshaling data for caching: %v", err)
    }

    expiration := time.Hour

    err = cachedUsers.Set(ctx, "users", string(cacheData), expiration).Err()
    if err != nil {
        return nil, fmt.Errorf("error caching data: %v", err)
    }

    return models.DatabaseUsersToUsers(users), nil

}

User结构体定义

type User struct {
    ID           uuid.UUID `json:"id"`
    FirstName    string    `json:"first_name"`
    LastName     string    `json:"last_name"`
    OtherName    string    `json:"other_name"`
    Username     string    `json:"user_name"`
    Password     string    `json:"password"`
    Email        string    `json:"email"`
    ProfileImage string    `json:"profile_image"`
    Status       string    `json:"status"`
    IsAdmin      bool      `json:"is_admin"`
    Role         string    `json:"role"`
    Gender       string    `json:"gender"`
    PhoneNumber  string    `json:"phone_number"`
    CreatedAt    time.Time `json:"created_at"`
    UpdatedAt    time.Time `json:"updated_at"`
}

问题根因

核心问题是缓存的数据与接口返回的数据类型不匹配:

  • 从Postgres查询到的users是数据库层结构体(推测为models.DatabaseUser),你直接将其序列化存入Redis
  • 从Redis取出反序列化时,却用了接口返回的models.User类型
  • 最终返回时又通过models.DatabaseUsersToUsers(users)做类型转换,这导致缓存的原始数据库结构体和API结构体的字段、JSON标签可能存在差异,反序列化时出现字段缺失

解决方案

将转换后的API结构体存入缓存,而非原始数据库结构体,修改后的代码如下:

func (usr *UserImplementation) GetAllUsers(ctx context.Context) ([]models.User, error) {
    cachedUsers, err := databaseapi.Redis_CacheDB_Api()
    if err != nil {
        return nil, fmt.Errorf("error connecting to Redis cache: %v", err)
    }

    cachedData, err := cachedUsers.Get(ctx, "users").Result()
    if err != nil && err != redis.Nil {
        return nil, fmt.Errorf("error retrieving cached data: %v", err)
    }

    if cachedData != "" {
        var cachedUsers []models.User
        err := json.Unmarshal([]byte(cachedData), &cachedUsers)
        if err != nil {
            return nil, fmt.Errorf("error unmarshaling cached data: %v", err)
        }
        return cachedUsers, nil
    }

    // 查询数据库原始数据
    dbUsers, err := usr.pg.Postgres_DB_Api().DB.GetAllUsers(ctx)
    if err != nil {
        return nil, fmt.Errorf("error: %v", err.Error())
    }

    // 先转换为API用的User类型
    apiUsers := models.DatabaseUsersToUsers(dbUsers)

    // 序列化转换后的API数据存入缓存
    cacheData, err := json.Marshal(apiUsers)
    if err != nil {
        return nil, fmt.Errorf("error marshaling data for caching: %v", err)
    }

    expiration := time.Hour
    err = cachedUsers.Set(ctx, "users", string(cacheData), expiration).Err()
    if err != nil {
        return nil, fmt.Errorf("error caching data: %v", err)
    }

    return apiUsers, nil
}

额外检查点

  1. 确认models.DatabaseUser和models.User的JSON标签完全对应,避免标签不一致导致反序列化字段缺失
  2. 检查DatabaseUsersToUsers转换函数是否正确处理了所有字段,无遗漏
  3. 确保需要缓存的字段都是导出状态(结构体字段首字母大写),未导出字段会被JSON序列化自动忽略

内容的提问来源于stack exchange,提问作者Daniel Dexoangle

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 14:23:09