玩转 Redis · Hash 篇

1 阅读5分钟

玩转 Redis · Hash 篇

环境:Docker 容器 redis-learn(Redis 7.4.11)

本篇地位:五大类型 2/5。一句话:存"一个东西的多个属性",默认用它


0. 一分钟回顾

痛点:用 String 存一个用户要贴 N 张便签(user:1:name、user:1:age…),碎、乱、整体过期难定。

Hash = 一张便签上画表格,三层结构:

key(表名)→ field(行名)→ value(格子内容)
me:profile →   name     →   fatcoder
               age      →   22

学名与比喻:官方名 Hash(因底层用"哈希表"技术实现,核心原理篇再讲),"表格"是使用时的心智模型——用时想表格,查文档搜 Hash。长得像 JS 对象 {name:"x", age:22}、Python 字典。


1. 命令全家桶(H 开头 = Hash 专属)

操作命令一句话说明
增/改HSET key f v [f v …]设置行(一条命令能设多行);同名 = 覆盖。返回新增行数
HSETNX key f v这行空才写(SETNX 的表格版)
HGET key f取一格(需要两个参数:表名+行名
HMGET key f1 f2取几格
HGETALL key整表倒出(行名、值交替输出)⚠️ 大表禁用
HKEYS key行名清单
HVALS key值清单
HLEN key有几行
HEXISTS key f行在不在(1/0)
HINCRBY key f n格内数字 ±n(新行自动从 0 起)
HDEL key f1 [f2…]删行(返回删掉几行)
DEL key删整表(通用指令)

教科书场景——购物车HSET cart:100 goods:1 2 goods:5 1HINCRBY cart:100 goods:1 1HGETALL cart:100 结算 → EXPIRE cart:100 2592000(30 天自动清空)。


2. 核心理解点(带练中亲手验证)

  1. HSET 返回"新增行数":更新已有行返回 0,但值确实改了(返回 0 ≠ 失败,HGET 验证)
  2. 格子存的都是字符串HINCRBY 等算术家族会自动"字符串↔数字"翻译(翻译→计算→存回,原子打包);翻不动就报 not an integer;普通命令(HGET)不翻译只念原文
  3. 空表自动消失:行删光 → key 本尊自动没了(String 不会这样,性格差异)
  4. 长度三兄弟HLEN=行数;STRLEN=String 值字节数;HSTRLEN key f=某格内容字节数
  5. HGETALL 大表 = 事故:单线程被长时间占用、全量传输、客户端内存风险——与 KEYS 同罪。大 N 禁全量,要几行用 HMGET,全表遍历用 HSCAN(后续讲)

3. 实操流水(真实命令 + 结果)

3.1 建表与批量加行

127.0.0.1:6379> HSET me:profile name "fatcoder"
1                                    ← 新增 1127.0.0.1:6379> HSET me:profile age "21" city "shanghai" hobby "balls"
3                                    ← 一条命令新增 3

3.2 读单行 / 整表

127.0.0.1:6379> HGET me:profile age
21
127.0.0.1:6379> HGETALL me:profile
name
fatcoder                             ← 行名、值交替排队输出
age
21
city
shanghai
hobby
balls

3.3 更新已有行:返回 0 但已生效

127.0.0.1:6379> HSET me:profile age 22
0                                    ← 新增 0 行(只是更新)
127.0.0.1:6379> HGET me:profile age
22                                   ← 亲眼验证:确实改了!

3.4 表内计数器:从没建过的行自动起步

127.0.0.1:6379> HINCRBY me:profile score 10
10                                   ← score 行不存在,自动从 0 起 +10
127.0.0.1:6379> HINCRBY me:profile score 10
20
127.0.0.1:6379> HINCRBY me:profile score 10
30
127.0.0.1:6379> HINCRBY me:profile score 10
40

3.5 删行 + 数行

127.0.0.1:6379> HDEL me:profile hobby
1                                    ← 删掉 1127.0.0.1:6379> HLEN me:profile
45 行变 4

3.6 空表自动消失(开篇演示 + 推理验证)

HSET user:100 name "小红" age 18 city "成都"   → 3
HDEL user:100 city                              → 1
HLEN user:1002
HDEL user:100 name age                          → 2
EXISTS user:1000    ← 行删光,key 自动没了

4. 待深入专区(挂起,学完 List 篇后回访)

Hash 格子只能存字符串,不能嵌套

  • 想塞"子表格":HSET k address HSET demo:sub x y → 报错(key 后参数必须"行名 值"成对,5 个散装参数凑不成对)
  • 存 JSON 字符串可行但有代价HSET k address '{"city":"shanghai","district":"pudong"}' → 存取都行,但 Redis 不懂内部结构,改一字段要整串读出→程序改→写回
  • 工程三解法:①拍平(city、district 平级两行)②分表(me:profile 主表 + me:address 单独一张,用 key 冒号层级替代内容嵌套)③存 JSON(整体取出、少改内部的场景)
  • 后续线索:RedisStack 的 RedisJSON 专门类型(生态篇)

5. 踩坑实录

现象教训
HGET 只给表名HGET me:profile → ERR 参数数量HGET 要两个参数:表名 + 行名
误读 HSET 返回 0以为更新失败0 = 新增 0 行 ≠ 失败;拿不准就 HGET 验证
误解 HLEN以为是字符串长度HLEN = 行数;量长度的是 STRLEN/HSTRLEN
以为格子里存的是数字score +10 能算存的是字符串,算术命令会自动翻译

6. 选型:String 贴 N 张 vs Hash 一张表

对比项StringHash
存对象 N 个属性N 条 key1 条 key N 行 ✅
只改一个属性SET user:1:age 19HSET user:1 age 19(平手)
整体过期挨条 EXPIRE ❌一次 EXPIRE ✅
知道有几个属性没办法HLEN
属性计数INCRHINCRBY(平手)

结论:存对象默认 Hash;单值/计数场景用 String。


7. 自检清单

  • HSET 更新已有行返回几?值到底改没改?怎么确认?
  • HGET 需要几个参数?分别是什么?
  • HLEN 量的是什么?
  • score 格子里存的是数字还是字符串?HINCRBY 靠什么算的?
  • 行删光后 key 还在吗?String 存空值呢?
  • 删一行用啥?删整表用啥?
  • 10 万行的表能 HGETALL 吗?三条理由?
  • 格子里能存子表格吗?工程上三种替代法?

8. 类型地图(2/5)

篇章形状典型用途招牌命令状态
📗 String一行字的纸条缓存、计数器SET GET INCR
📘 Hash小表格(字段↔值)对象、单字段修改、购物车HSET HGET HINCRBY
📙 List两头进出的队伍消息队列、最新列表LPUSH RPOP⬅️ 下一站
📕 Set不重复的一筐球去重、标签、共同好友SADD SINTER
📓 ZSet带分数自动排队排行榜、优先级ZADD ZRANGE