Redis 哈希命令详解
hash 是 Redis 的第二大类型,适合存"对象"——一个 key 下有多个 field-value,就像在 Redis 里直接放了一个小对象。本篇把 hash 的常用命令完整过一遍。
1. 最基础的 HSET / HGET / HGETALL
# hash:一个 key 下多个 field-value,像一个小对象
HSET user:1 name "小明" age 20 city "北京"
# 返回新增字段数:3
HGET user:1 name # 读单个字段
"小明"
HGET user:1 age
"20"
HGETALL user:1 # 读所有字段和值
# 1) "name"
# 2) "小明"
# 3) "age"
# 4) "20"
# 5) "city"
# 6) "北京"
HMGET user:1 name city # 一次读多个字段
# 1) "小明"
# 2) "北京"
HLEN user:1 # 字段数:3
HEXISTS user:1 age # 字段是否存在:1 (true)
HKEYS user:1 # 所有字段名
HVALS user:1 # 所有字段值命名习惯:用冒号分隔的层级前缀(如 user:1、goods:100)作为 key,field 用属性名(name、age、price)。这样在图形客户端里看起来就像一棵树,便于团队协作。
HGETALL 一次返回所有字段和值,交替排列(field1, value1, field2, value2, ...)。注意它的返回顺序不保证——hash 字段无序存储,不能依赖顺序。生产环境如果 hash 字段非常多(几千几万个),HGETALL 会阻塞 Redis,要用 HSCAN 增量遍历。
2. 字段级增删改
# 修改某个字段(只改这一个,不影响其他字段)
HSET user:1 age 21 # 把 age 从 20 改成 21
# 新增字段(在原有 hash 上追加)
HSET user:1 email "a@b.com"
# 字段不存在时才设置
HSETNX user:1 age 25 # age 已存在 -> 不修改,返回 0
HSETNX user:1 phone "138" # phone 不存在 -> 新增,返回 1
# 删除字段
HDEL user:1 email phone # 返回成功删除的字段数
# 完全删除整个 hash
DEL user:1 # 用通用 DEL 命令hash 的核心优势就在这里:单字段操作。要改用户的年龄,只需 HSET user:1 age 21 一行,其他字段完全不动。这比"string + JSON"方案高效得多——后者必须读出整个 JSON、解析、改字段、再写回。
3. 字段值自增:HINCRBY
和 string 的 INCR 一样,hash 的某个整数字段也能原子自增。这让 hash 成了"多维计数器"的完美载体。
# 字段值自增(必须是整数,否则报错)
HSET stats:day:20240801 pv 0 uv 0
HINCRBY stats:day:20240801 pv 1 # pv +1
HINCRBY stats:day:20240801 pv 99 # pv +99
HINCRBY stats:day:20240801 uv 1 # uv +1
HINCRBYFLOAT goods:1 price 1.5 # 浮点数自增
# 用 hash 做多维计数器:
# 每个 key 是一个维度(如某天),每个 field 是子指标
HSET stats:day:20240801 pv 0 uv 0 click 0 order 0
HINCRBY stats:day:20240801 click 1
HGETALL stats:day:20240801 # 一眼看到这天所有指标电商网站的"今日数据看板"经常这么设计:每天一个 hash key,field 是各种指标(PV/UV/点击/下单/支付)。每次用户行为触发 HINCRBY,实时数据看板直接 HGETALL 一次拉出所有指标,无需复杂查询。
4. 字符串字段操作与 HSCAN
# 取字段值的子串(不读出整个值,大字段时省内存)
HSET user:1 bio "Hello, I am a developer"
HGETRANGE user:1 bio 0 4 # "Hello"
HSTRLEN user:1 bio # 字段值长度:27
# HSCAN:遍历大 hash(字段极多时用,类似 SCAN)
# 返回游标 + 一批 field-value
HSCAN user:1 0 COUNT 10
# 1) "0" # 下次游标(0 表示遍历完)
# 2) 1) "name"
# 2) "小明"
# 3) "age"
# 4) "20"
# ...HSCAN 是游标式遍历——给定一个游标(从 0 开始),返回新游标和一批字段。重复调用直到新游标为 0,表示遍历完。它的好处是不阻塞 Redis,即使 hash 有上百万字段也能稳定遍历。生产规则:遍历大 hash 永远用 HSCAN,不用 HGETALL / HKEYS / HVALS。
5. hash vs string+JSON
新手最常问:"存一个对象,我用 hash 还是用 string 存序列化后的 JSON?"答案分情况,但多数情况用 hash 更好。
# 反例:把整个对象序列化成 string 存
SET user:1 '{"name":"小明","age":20,"city":"北京"}'
GET user:1
# 要改一个字段(name),必须:
# 1. GET 整个 JSON
# 2. 解析 JSON
# 3. 改字段
# 4. 序列化回 JSON
# 5. SET 写回
# 既慢又有并发覆盖风险(乐观锁/悲观锁烦死人)。
# 正例:用 hash,改字段就一行
HSET user:1 name "小红"
# Redis 直接改这个 field,O(1),无需读出整体
# 内存对比:
# 字段较少(< 128 个)、值较短(< 64 字节)时,
# Redis 把 hash 压缩成 ziplist/listpack 存储,极省内存。
# 字段多了会自动升级成 hashtable(标准哈希表)。什么时候反而用 string+JSON 合适?几个场景:对象很小且整体读写(比如一个不常改的配置)、嵌套层级深(hash 不支持嵌套,嵌套对象要先打平)、频繁整体序列化传输(反正 API 返回 JSON,直接存 JSON 省一次反序列化)。权衡的关键是"是否需要单字段操作"——需要就用 hash,不需要就用 string。
6. 典型应用场景
- 用户/商品/文章详情:每个实体一个 hash,key 用
类型:ID。 - 多维计数器:按时间维度建 key,field 是子指标(如
stats:day:20240801)。 - 配置中心:把应用配置按环境存成 hash(
config:prod),改一项无需重启。 - 购物车:
cart:用户ID一个 hash,field 是商品 ID,value 是数量。增删改商品都是 O(1)。 - 分布式 Session:把会话的多个字段(用户 ID、登录时间、token 等)存一个 hash。
小结
hash 类型非常适合对象存储和多维计数。核心命令:HSET/HGET/HGETALL(读写)、HMGET(批量读)、HDEL(删字段)、HINCRBY(字段自增)、HSCAN(安全遍历)。下一篇我们继续看两个相关的"集合"类型——list 和 set。
← 上一篇 Redis 字符串命令详解
下一篇 Redis 列表与集合命令 →