Hash 对象
HSET/HGETALL、存储结构化实体。
Hash 对象:用户资料与做题进度缓存
这一课你将能做到
- 用 Hash 命令对「对象」按字段读写、批量取、原子增减
- 为本站设计一个可局部更新的做题进度缓存结构
- 说清楚 Hash 与「String 存 JSON」各自的适用边界
为什么要学
真实业务里大量数据长着「对象」的样子:一个用户有昵称、头像、等级、连胜天数;一门课的进度有已做几题、正确率、最后学习时间。
如果用 String 存 JSON,改一个字段就得把整个对象读出来、解析、改、序列化、再写回去——一次小改动牵动整个值,并发写还会互相覆盖。Hash 把一个键下的多个「字段-值」分开存放,改昵称只动昵称那个字段,取正确率只取一个字段,字段级的增量更新还是原子的。
本站的做题进度是高频写场景:用户每通过一道题,进度里的 solved 要加一、acRate 要重算、lastActive 要更新时间。Hash 让这三次操作互不干扰、各自极小。
先建立图像
String 存 JSON 的样子:
progress:user:1001:course:7 → "{\"solved\":12,\"total\":50,\"acRate\":0.75,...}"
改 solved?整个值重写。
Hash 的样子:
progress:user:1001:course:7
┌──────────┬────────┐
│ field │ value │
├──────────┼────────┤
│ solved │ 12 │ ← HINCRBY 单独 +1
│ total │ 50 │
│ acRate │ 0.75 │
│ status │ doing │
│ lastActive│ ... │
└──────────┴────────┘
每个字段独立读写,互不影响
跟着做
步骤 1:写入和读取字段
127.0.0.1:6379> HSET user:1001 nickname 小明 level 3 streak 7
(integer) 3
127.0.0.1:6379> HGET user:1001 nickname
"小明"
127.0.0.1:6379> HGET user:1001 vip
(nil)
HSET 一次可写多个字段;字段已存在就是更新,新字段返回新增数量。Redis 4 以后 HSET 已经覆盖了老的 HMSET,统一用 HSET 即可。
判断字段是否存在、看字段总数:
127.0.0.1:6379> HEXISTS user:1001 vip
(integer) 0
127.0.0.1:6379> HLEN user:1001
(integer) 3
步骤 2:批量取与全量取
127.0.0.1:6379> HMGET user:1001 nickname level streak vip
1) "小明"
2) "3"
3) "7"
4) (nil)
127.0.0.1:6379> HGETALL user:1001
1) "nickname"
2) "小明"
3) "level"
4) "3"
5) "streak"
6) "7"
HGETALL 返回「字段、值、字段、值」交替的数组。只想要字段名或值分别用 HKEYS、HVALS。字段特别多的 Hash,用 HSCAN 分批:
127.0.0.1:6379> HSCAN user:1001 0 COUNT 100
步骤 3:字段级原子增减
用户通过一道题:
127.0.0.1:6379> HSET progress:user:1001:course:7 solved 12 total 50 acRate 0.75 status doing
OK
127.0.0.1:6379> HINCRBY progress:user:1001:course:7 solved 1
(integer) 13
127.0.0.1:6379> HINCRBYFLOAT progress:user:1001:course:7 acRate 0.02
"0.77"