Redis 哈希(Hash)完全攻略
“轻量级对象存储神器” —— 比 String 更省内存、更新更灵活、适合存用户、商品、配置等结构化数据。
一、Hash 核心特点
| 特性 | 说明 |
|---|---|
| 结构 | key → {field1: value1, field2: value2, ...} |
| 最大字段数 | 4,294,967,295(约 40 亿) |
| 每个 value 最大 | 512 MB |
| 内部编码 | ziplist(小对象) ↔ hashtable(大对象) |
| 所有操作 O(1) | 增删改查极快 |
| 内存友好 | 比 String 存对象 节省 50%+ 内存 |
二、Hash 命令全表(共 20+)
| 命令 | 说明 | 示例 |
|---|---|---|
HSET key field value | 设置字段 | HSET user:1001 name "张三" |
HGET key field | 获取字段 | HGET user:1001 name |
HMSET key f1 v1 f2 v2 | 批量设置(7.0 前) | → 用 HSET |
HMGET key f1 f2 | 批量获取 | HMGET user:1001 name age |
HGETALL key | 获取全部字段和值 | HGETALL user:1001 |
HDEL key field [field ...] | 删除字段 | HDEL user:1001 temp |
HINCRBY key field n | 字段自增整数 | HINCRBY user:1001 score 10 |
HINCRBYFLOAT key field 1.5 | 浮点数自增 | HINCRBYFLOAT user:1001 balance 9.9 |
HEXISTS key field | 字段是否存在 | HEXISTS user:1001 name |
HLEN key | 字段数量 | HLEN user:1001 |
HKEYS key | 所有字段名 | HKEYS user:1001 |
HVALS key | 所有值 | HVALS user:1001 |
HSTRLEN key field | 字段值长度 | HSTRLEN user:1001 name |
HSETNX key field value | 不存在才设置 | HSETNX user:1001 status "new" |
HSCAN key cursor [MATCH pattern] | 渐进式遍历 | HSCAN user:1001 0 MATCH *na* |
三、核心实战场景
1. 用户信息存储(最经典)
HSET user:1001 name "李四" age 28 email "li@example.com" score 95.5
HINCRBY user:1001 age 1
HGETALL user:1001
# 1) "name" 2) "李四" 3) "age" 4) "29" 5) "email" ...
比 String 存 JSON 节省 60% 内存!
2. 商品属性
HSET product:2001 name "Redis 实战" price 79.9 stock 100 tags "cache,db"
HINCRBY product:2001 stock -1
HGET product:2001 price
3. 配置中心
HSET config:api rate_limit 1000 timeout 30
HGET config:api rate_limit
HINCRBY config:api rate_limit 500
4. 购物车(小型)
HSET cart:1001 item:1 2 item:2 1 # 商品1买2个,商品2买1个
HINCRBY cart:1001 item:1 1 # 再加1个
HGETALL cart:1001
大型购物车建议用
Hash + List或数据库
5. 动态统计面板
HINCRBY dashboard:20251112 pv 1
HINCRBY dashboard:20251112 uv 1
HINCRBY dashboard:20251112 error 1
四、Hash vs String 对比(何时用哪个?)
| 对比项 | Hash | String |
|---|---|---|
| 存储对象 | 推荐 | 存 JSON |
| 部分更新 | 支持(只改一个 field) | 需重写整个值 |
| 内存占用 | 更省(小对象用 ziplist) | 更大 |
| 字段多于 100 个 | 推荐 | 不推荐 |
| 需要原子递增字段 | 支持 HINCRBY | 需 Lua |
| 简单值/大文本 | 不推荐 | 推荐 |
结论:对象用 Hash,纯文本/大 JSON 用 String
五、内部编码优化(ziplist vs hashtable)
# 小 Hash → ziplist(省内存)
HSET small:1 a 1 b 2 c 3
OBJECT ENCODING small:1 → "ziplist"
# 大 Hash → hashtable
HSET large:1 {1..1000 fields}
OBJECT ENCODING large:1 → "hashtable"
切换阈值(hash-max-ziplist-entries)
| 配置 | 默认值 | 说明 |
|---|---|---|
hash-max-ziplist-entries | 512 | 字段数 > 512 → hashtable |
hash-max-ziplist-value | 64 | 单个 value > 64 字节 → hashtable |
生产建议:保持默认,或调大到
1024提升性能
六、性能与内存优化建议
| 建议 | 说明 |
|---|---|
| 控制字段数量 < 1000 | 避免 hashtable 扩容 |
| 避免超大 value | >1MB 建议用 String 或分片 |
| 使用 Pipeline | 批量 HSET/HMGET |
| 设置 TTL 到 key | EXPIRE user:1001 86400 |
| HSCAN 替代 HGETALL | 大 Hash 遍历用 HSCAN |
# Pipeline 批量更新
pipe = r.pipeline()
pipe.hset('user:1', 'score', 100)
pipe.hset('user:1', 'level', 5)
pipe.expire('user:1', 3600)
pipe.execute()
七、大 key 排查与拆分
排查大 Hash
redis-cli --bigkeys
# 输出:Biggest hash found "user:stats" with 50000 fields
拆分方案
# 原:HSET user:1001 profile:... stats:... prefs:...
# 拆:
HSET user:1001:profile name "张三" age 25
HSET user:1001:stats login_count 10 last_login 1734028800
HSET user:1001:prefs theme "dark" lang "zh"
八、一键速查表
# 基础操作
HSET u:1 name "张三" age 25
HGET u:1 name
HGETALL u:1
HDEL u:1 temp
# 批量
HMGET u:1 name age email
HINCRBY u:1 age 1
# 遍历(安全)
HSCAN u:1 0 MATCH *na* COUNT 10
# 管理
HLEN u:1
HEXISTS u:1 name
HKEYS u:1
HVALS u:1
九、客户端代码示例
Python (redis-py)
import redis
r = redis.Redis()
# 创建用户
r.hset('user:1001', mapping={
'name': '王五',
'age': 30,
'score': 98.5
})
# 更新分数
r.hincrby('user:1001', 'score', 1.5)
# 获取部分字段
info = r.hmget('user:1001', 'name', 'score')
print(info) # [b'王五', b'100.0']
# 设置过期
r.expire('user:1001', 3600)
Go (go-redis)
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{Addr: "localhost:6379"})
rdb.HSet(ctx, "user:1001", map[string]interface{}{
"name": "李四", "age": 28,
})
rdb.HIncrBy(ctx, "user:1001", "age", 1)
十、常见问题与解决方案
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 内存暴涨 | Hash 字段太多 | 拆分 + TTL |
HGETALL 卡顿 | 字段 > 10万 | 改用 HSCAN |
| 字段冲突 | 命名不规范 | 统一前缀 |
| 更新慢 | 频繁全量 HGETALL | 缓存热点字段 |
十一、Hash 在高并发场景中的妙用
1. 原子更新多个字段
# 事务(不推荐,性能差)
MULTI
HSET u:1 a 1
HSET u:1 b 2
EXEC
# 推荐:Pipeline + HSET
2. 结合 Lua 实现复杂逻辑
EVAL "
local user = KEYS[1]
local score = redis.call('HINCRBY', user, 'score', ARGV[1])
if score > 1000 then
redis.call('HSET', user, 'level', 'VIP')
end
return score
" 1 user:1001 10
完成!你已精通 Redis Hash!
# 一行命令体验 Hash 全功能
redis-cli <<EOF
HSET u:1 name "Redis" age 7
HINCRBY u:1 age 1
HGETALL u:1
HLEN u:1
HSCAN u:1 0
EXPIRE u:1 60
EOF
下一步推荐:
需要我送你:
- “用户中心完整 Hash 设计方案”?
- “高性能配置中心(Hash + 热加载)”?
- “购物车系统(Hash + ZSet)”?
回复:用户中心 | 配置中心 | 购物车 即可!