反序列化前必须确认redis中存储的原始格式:若存的是json.marshal结果,就读取后用json.unmarshal;若为gob.encode,则需gob.decode并提前注册类型;纯字符串则直接转string;格式不匹配会导致解码失败。

反序列化前先确认 Redis 存储格式
Go 从 Redis 读出来的 []byte 是原始字节,不带类型信息。你得知道当初存进去的是什么格式——是 json.Marshal 还是 gob.Encode,或者直接用 strconv.AppendInt 拼的字符串?否则反序列化必失败。
常见错误现象:json: cannot unmarshal string into Go value of type map[string]interface{},说明你拿 JSON 字节去解到了非结构体上;或者 gob: unknown type id,说明存的时候没用 gob,却硬要用它解。
- 如果存时用了
json.Marshal,读出来后必须用json.Unmarshal解到对应 struct 或map[string]interface{} - 如果存的是纯字符串(比如
client.Set(ctx, "key", "hello", 0)),client.Get(ctx, "key").Bytes()得到的是 UTF-8 字节,直接string(b)即可,不需要“反序列化” - 若用
encoding/gob,注意 gob 不跨语言、不兼容 Go 版本升级,且必须提前注册类型(gob.Register(&MyStruct{}))
用 json.Unmarshal 处理最常见场景
绝大多数 Go 服务用 Redis 缓存结构化数据时,选的是 JSON。这时读取后直接 json.Unmarshal,但要注意两点:nil 切片、空值、字段 tag。
示例:
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
// 假设存入:client.Set(ctx, "user:123", []byte(`{"id":123,"name":"alice"}`), 0)
val, err := client.Get(ctx, "user:123").Bytes()
if err != nil {
// 处理 key 不存在或连接错误
}
var u struct {
ID int `json:"id"`
Name string `json:"name"`
}
if err := json.Unmarshal(val, &u); err != nil {
// 注意:err 可能是 io.EOF(空值)、json.SyntaxError(Redis 里存了脏数据)
}
- 务必传指针
&u,否则解包无效 - 如果 Redis 中该 key 为空(
nil),.Bytes()返回nil,json.Unmarshal(nil, &u)会 panic —— 先判空:if len(val) == 0 { ... } - struct 字段必须导出(首字母大写),且 tag 名要和 JSON 字段一致,否则字段被忽略
处理 []byte 直接存的 gob 或自定义二进制格式
gob 适合 Go 内部服务间通信,但对 Redis 来说不够通用。如果你真用了 gob,读取后不能直接当字符串用,必须用 gob.NewDecoder 配合 bytes.Reader。
示例:
val, _ := client.Get(ctx, "data").Bytes()
var data MyStruct
dec := gob.NewDecoder(bytes.NewReader(val))
if err := dec.Decode(&data); err != nil {
// 常见错误:gob: cannot decode into nil pointer —— data 必须已初始化(如 var data MyStruct)
// 或者类型未注册:gob: unknown type id 123 —— 确保存和取用的是同一版本的 struct,且调用过 gob.Register
- gob 的
Decode要求目标变量已分配内存,不能传nil指针 - struct 字段名变更、新增未导出字段、Go 版本升级都可能导致 gob 解码失败,生产环境慎用
- 如果 Redis 里存的是加密或 base64 编码的二进制(比如 AES 加密后的 []byte),先 base64.StdEncoding.DecodeString,再解密,最后才是反序列化
警惕 Redis GET 返回的 nil 和类型混淆
Redis 的 GET 命令在 key 不存在时返回 (nil),Go 客户端(如 github.com/redis/go-redis)会把这转成 redis.Nil 错误,而不是 nil []byte。很多人直接调 .Bytes() 不检查 error,结果 panic。
- 正确姿势:始终先检查
err == redis.Nil,再决定是否继续反序列化 -
client.Get(ctx, key).Result()返回string,但如果你需要原始字节(比如存的是图片或 protobuf),必须用.Bytes(),不能用.String()—— 后者会做 UTF-8 验证,遇到非法字节直接报错 - 某些客户端(如 redigo)返回
interface{},需类型断言为[]byte,断言失败会 panic,务必加 ok 判断
真正麻烦的不是反序列化本身,而是搞不清 Redis 里到底存了什么格式、有没有被中间件改写、是否经过压缩或加密。先 redis-cli get key 看一眼原始内容,比瞎猜快十倍。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










