news 2026/5/1 8:01:58

Redis 核心数据结构(三)——Hash,把一堆字段塞进一个 Key

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Redis 核心数据结构(三)——Hash,把一堆字段塞进一个 Key

对象无需再存 JSON 字符串了,Hash 让你直接改里面的某个字段,不用全量覆盖。

本次导航

  • Hash 长什么样(像极了 Python 的字典)
  • 核心命令:HSETHGETHGETALLHINCRBY
  • 内部编码:什么时候省内存,什么时候变哈希表
  • 哈希字段过期(Redis 7.2+ 的HEXPIRE,告别整 Key 过期)
  • 实战场景:用户信息、对象缓存、购物车
  • Hash vs String 存 JSON:谁更香?

发车前提醒:先玩熟 String 和 List,Hash 就在它们之间。

一、Hash 就是 Redis 里的“小字典”

在一个 Redis Key 下面,再挂一堆 field-value 对。很像编程语言里的字典或者对象。

HSET user:1001 name"张三"age25city"北京"

这样,user:1001这个 Key 下,有三个字段:nameagecity。你可以单独改age,不用把整个对象重新存一遍。

和 String 存 JSON 的区别

  • String 存 JSON:改一个字段,你得取出整个 JSON,反序列化,改完再序列化,再SET回去。
  • Hash:直接HSET user:1001 age 26,一步到位。

二、核心命令

命令功能
HSET key field value [field value ...]设置一个或多个字段
HGET key field获取指定字段
HGETALL key获取所有字段和值(生产环境慎用,字段多会慢)
HMGET key field [field ...]获取多个字段
HDEL key field [field ...]删除一个或多个字段
HEXISTS key field判断字段是否存在
HINCRBY key field increment字段值增加指定整数
HINCRBYFLOAT增加浮点数
HLEN key获取字段数量
HKEYS/HVALS获取所有字段名 / 所有值

动手试试

# 塞一个用户HSET user:1001 name"李四"age30balance1000# 单独取年龄HGET user:1001 age# "30"# 给余额加 200HINCRBY user:1001 balance200# 返回 1200# 同时获取多个字段HMGET user:1001 name balance# ["李四", "1200"]# 看看都有哪些字段HKEYS user:1001# ["name","age","balance"]# 删除年龄字段HDEL user:1001 age HGETALL user:1001# 只剩 name 和 balance

注意HGETALL当字段非常多(比如几百上千)时,可能会阻塞 Redis。生产环境多用HMGET取你真正需要的字段。

127.0.0.1:6379> HSET user:1001 name"李四"age 30 balance 1000(integer)1 127.0.0.1:6379> HGET user:1001 age"30"127.0.0.1:6379> HINCRBY user:1001 balance 200(integer)1200 127.0.0.1:6379> HMGET user:1001 name balance 1)"\xe6\x9d\x8e\xe5\x9b\x9b"2)"1200"127.0.0.1:6379> HKEYS user:1001 1)"name"2)"age"3)"city"4)"balance"127.0.0.1:6379> HDEL user:1001 age(integer)1 127.0.0.1:6379> HGETALL user:1001 1)"name"2)"\xe6\x9d\x8e\xe5\x9b\x9b"3)"city"4)"\xe5\x8c\x97\xe4\xba\xac"5)"balance"6)"1200"

三、内部编码

Hash 底层有两种存储方式:

编码条件说明
listpack字段和值都比较小,数量少连续内存,省空间
hashtable超过配置阈值(默认512字节或64个字段)转为真正的哈希表,读写 O(1)

你可以用OBJECT ENCODING key查看:

HSET small a1b2OBJECT ENCODING small# "listpack"# 塞一个长字符串,超过阈值HSET big c"为给定哈希键的一个或多个字段设置过期时间(TTL 或生存时间)。您必须至少指定一个字段。字段的 TTL 到期时将自动从哈希键中删除。字段的过期时间仅会被删除或覆盖哈希字段内容的命令清除,包括 HDEL 和 HSET 命令。这意味着所有在概念上修改哈希键字段值而不替换为新值的操作都不会影响 TTL。您可以使用 HPERSIST 命令清除 TTL,该命令会将哈希字段变回持久字段。请注意,使用零 TTL 调用 HEXPIRE/HPEXPIRE 或使用过去的 Unix 时间调用 HEXPIREAT/HPEXPIREAT 将导致哈希字段被删除。HEXPIRE 命令支持一组选项:NX -- 对于每个指定字段,仅当字段没有过期时间时设置过期时间。XX -- 对于每个指定字段,仅当字段已存在过期时间时设置过期时间。GT -- 对于每个指定字段,仅当新的过期时间大于当前过期时间时设置过期时间。LT -- 对于每个指定字段,仅当新的过期时间小于当前过期时间时设置过期时间。对于 GT 和 LT 选项,非易失性字段被视为具有无限 TTL。NX、XX、GT 和 LT 选项是互斥的。您可以将已设置 TTL 的字段作为参数调用 HEXPIRE。在这种情况下,生存时间将被更新为新值。"OBJECT ENCODING big# "hashtable"

不用你操心,Redis 自动升级底层的存储方式。

127.0.0.1:6379> object encoding small"listpack"127.0.0.1:6379> object encoding big"hashtable"

四、哈希字段过期(Redis 7.4+ 的神器)

在老版本里,整个 Hash 要么一起活,要么一起死。你不能让user:1001temp_token字段 5 分钟失效,而name永久保留。

Redis 7.4引入了字段级过期,命令如下:

HEXPIRE key seconds[NX|XX|GT|LT]FIELDS numfields field[field...]

参数说明

  • key: 哈希键名称。
  • seconds: TTL(以秒为单位)。
  • FIELDS numfields field [field …]: 指定字段及其数量。

可选参数

  • NX: 仅当字段没有过期时间时设置。
  • XX: 仅当字段已有过期时间时设置。
  • GT: 新的过期时间必须大于当前过期时间。
  • LT: 新的过期时间必须小于当前过期时间。

示例:

# 为不存在的键设置过期时间HEXPIRE no-key20NX FIELDS2field1 field2(nil)# 创建一个哈希键并设置字段HSET examplekey field1"hello"field2"world"(integer)2# 为字段设置 10 秒的过期时间HEXPIRE examplekey10FIELDS3field1 field2 field31)(integer)1# field1 的过期时间成功设置2)(integer)1# field2 的过期时间成功设置3)(integer)-2# field3 不存在,返回 -2# 检查哈希键内容HGETALL mykey(empty array)

画个图,更直观的了解下:

场景:用户登录后存session_id在 Hash 里,设置 30 分钟过期,同时保留用户基本资料。以前你要单独存一个 String Key,现在一个 Hash 搞定。

五、实战场景

场景1:用户信息 / 对象缓存

# 存用户资料HSET user:1001 name"王五"email"wang@example.com"points100# 增加积分HINCRBY user:1001 points50# 修改邮箱(只改一个字段)HSET user:1001 email"newwang@example.com"# 取出来展示HGETALL user:1001

场景2:购物车(经典案例)

每个用户的购物车是一个 Hash,field是商品 ID,value是数量:

# 用户1001 添加 2个商品101,1个商品102HSET cart:1001 prod_1012HINCRBY cart:1001 prod_1021# 增加某种商品数量HINCRBY cart:1001 prod_1011# 现在变成3# 减少数量HINCRBY cart:1001 prod_101-1# 删除商品HDEL cart:1001 prod_102# 查看购物车所有商品HGETALL cart:1001

购物车天然适合 Hash,商品数量可以单独增减,不需要序列化整个数组。

场景3:配置项 / 属性存储

比如存某个服务的开关配置:

HSET config:service_atimeout30retry3enable_ssl1HGET config:service_atimeout

随时改单条配置,不用重写整个配置对象。

六、Hash vs String 存 JSON:对决

假设你要存一个用户对象:{"name":"张三","age":25,"city":"北京"}

方式存数据改 age读 age内存占用
String + JSONSET user:1001 '{"name":"...","age":25,...}'取出→反序列化→改→序列化→SETGET后解析序列化后的字符串
HashHSET user:1001 name "张三" age 25 city "北京"HSET user:1001 age 26HGET user:1001 age每个 field 单独存,但压缩后更省

结论

  • 如果你经常单独修改某个字段,Hash 完胜。
  • 如果你每次都整体读写(比如整个对象传给前端),String + JSON 可能更方便。
  • 内存方面,小对象 Hash 的编码非常省;大对象两者差别不大。

推荐:对象字段少、频繁单独改 → Hash。对象大、嵌套深、整体读为主 → String + JSON。


📢 点关注,不迷路

如果你看到这儿了,欢迎点赞 + 关注,我们下期见~

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/1 7:54:06

Qwen2.5-Coder与TensorRT-LLM前瞻解码优化实践

1. Qwen2.5-Coder与TensorRT-LLM的协同优化实践在当今AI辅助编程领域,大语言模型正逐步改变开发者的工作流。作为这一趋势的代表,Qwen团队最新推出的Qwen2.5-Coder系列模型在代码生成、逻辑推理和错误修复等任务上展现了卓越性能。本文将深入探讨如何通过…

作者头像 李华
网站建设 2026/5/1 7:49:01

未来的智能体不仅有预训练、还有边训练和后训练

现在的智能体早就不是“出厂即巅峰”了,边训练和后训练正在彻底改变我们使用AI的方式。简单来说,这三者的分工其实特别清晰。预训练是打地基,让智能体拥有了基础的逻辑和知识储备。边训练是实战中的进化,智能体在和你互动的过程中…

作者头像 李华
网站建设 2026/5/1 7:41:37

CGA 老年人能力评估助力养老服务精准化

当前社会老龄化程度不断加深,养老服务的核心需求从“有保障”转向“更精准”,CGA老年人能力评估成为衔接老年群体需求与养老服务供给的关键纽带。依托科学的测评逻辑与智能系统支撑,CGA老年人能力评估打破传统养老服务的粗放模式,…

作者头像 李华