redis-go v9 默认支持 hset/hget 等哈希命令,需用 redis.client 实例调用;hset 支持单字段或 map 批量写入,map 必须作为整体参数;hget 返回空字符串而非 nil 表示字段不存在;hgetall 返回 map[string]string 需手动类型转换。

Go 用 redis-go 连 Redis 哈希表,先确认客户端是否支持 HSet/HGet
redis-go(github.com/go-redis/redis/v9)默认支持所有哈希命令,但必须用 redis.Client 实例调用,不能误用 redis.NewClient() 返回的旧版接口(v8 及以前)。v9 中所有命令都在 client.HSet()、client.HGet() 等方法里,不是 client.Set() 的变体。
常见错误是:连上 Redis 后直接写 client.Set("user:1", "name", "alice")——这其实是字符串操作,不是哈希字段。哈希必须用 HSet 和 HGet,且 key 是哈希结构的顶层名(比如 "user:1"),field 才是字段名(比如 "name" 或 "age")。
HSet 写入多个字段时,参数顺序和类型容易出错
HSet 支持单字段或批量写入,但签名是 HSet(ctx, key, field interface{}, value interface{}) 或 HSet(ctx, key, values map[string]interface{})。别把 map 当成可变参数传——下面这样会 panic:
// ❌ 错误:把 map 当作 ...interface{} 传
client.HSet(ctx, "user:1", map[string]interface{}{"name": "alice", "age": 25})
// ✅ 正确:用 map 形式传参
client.HSet(ctx, "user:1", map[string]interface{}{"name": "alice", "age": 25})
// ✅ 或者单字段写法
client.HSet(ctx, "user:1", "email", "alice@example.com")
注意:values map[string]interface{} 中的 key 必须是 string,value 会自动转成 Redis 字符串(即调用 fmt.Sprintf("%v", v)),所以 int64、bool 都可以,但结构体要自己序列化(比如用 json.Marshal 后存字符串)。
HGet 读单个字段返回的是 string,空字段返回空字符串而非 nil
HGet(ctx, key, field) 返回 *redis.StringCmd,调用 Result() 得到 (string, error)。如果字段不存在,string 是 "",error 是 nil —— 不是 redis.Nil。这点和 Get() 不同,容易误判为“读取失败”:
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- 字段存在且值为空字符串 → 返回
"",nil - 字段根本不存在 → 也返回
"",nil - 只有 key 不存在(整个哈希都不存在)→ 才返回
redis.Nil
所以判断字段是否存在,得用 HExists;判断值是否为空字符串,得靠业务逻辑区分。例如:
val, err := client.HGet(ctx, "user:1", "phone").Result()
if err == redis.Nil {
// 整个 user:1 哈希都不存在
} else if err != nil {
// 真正的网络或协议错误
} else {
// val 是 string,可能是 ""(字段没设过,或设了空字符串)
// 要确认字段是否存在,再查一次:
exists, _ := client.HExists(ctx, "user:1", "phone").Result()
}
HGetAll 一次性读全部字段,但返回 map[string]string,注意类型转换
如果想读整个哈希,用 HGetAll(ctx, key),它返回 *redis.StringStringMapCmd,Result() 得到 map[string]string。所有 value 都是字符串,哪怕你存的是数字:
data, _ := client.HGetAll(ctx, "user:1").Result() // data["age"] == "25"(字符串),不是 int age, _ := strconv.Atoi(data["age"]) // 需手动转
性能上,HGetAll 比多次 HGet 更省 round-trip,但内存占用略高;如果只读两三个字段,用多次 HGet 更清晰。另外,Redis 哈希字段数超几千时,HGetAll 可能变慢,这时建议用 HScan 分批拉取。
哈希结构本身不支持嵌套,也不支持 TTL —— TTL 只能设在整个 key 上(EXPIRE),所以别指望给某个 field 单独设过期时间。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










