很多人把 Hash 当成「Redis 里的 Map」,于是自然会拿它存用户画像、购物车、配置项。这个方向通常是对的,但真正关键不在 HSET/HGET,而在小 Hash 能否留在 listpack 编码里。
当字段数量和字段长度还小,Hash 可以把多个 field/value 挤进一块连续内存,省掉大量 key 和对象头;一旦越过阈值,它会切换成 hashtable,查找变快,内存开销也随之上来。
先把机制边界说清楚
这一篇讨论的是普通 Hash 的编码和工程边界,不讨论 RedisJSON 这种层级文档模型。Hash 适合扁平对象和字段级更新,不适合无限扩张成一个跨业务大表。
整体路径
上面这张图先把主线铺开:小对象省内存,大对象换查询性能。读 Redis 这类系统,最重要的是别只停在命令接口,要继续追问它在内存里是什么形状、在主线程上走多远、失败时会留下什么状态。
底层机制
- listpack 编码把 field 和 value 顺序压在连续内存里,适合字段少、值短的小对象。
- 字段数量或单个 value 长度超过配置阈值后,Hash 会转换成 hashtable。
- hashtable 让单字段查找接近 O(1),但每个字段都要承担字典节点、SDS 和分配器对齐成本。
HSCAN是渐进式遍历工具,不是强一致快照;遍历过程中新增或删除字段可能影响结果。
这些机制放在一起看,就能把「这个命令能不能用」改成「这个命令在当前数据规模下还便不便宜」。Redis 的很多坑,不是命令本身错了,而是数据规模和访问方式已经越过了它的舒适区。
取舍与边界
Hash 的好处是把「大量小 key」收束成「一个对象」。但一个超级 Hash 又会把 rehash、迁移、备份和 HGETALL 的成本集中到一个 key 上。
典型问题:用机制化例子排查
- 用户画像、购物车、配置项这类字段级读写优先考虑 Hash,而不是整块 JSON String。
- 大对象禁止默认
HGETALL,接口层要按字段读取,后台任务也要分批HSCAN。 - 字段数量过大时按业务维度拆 key,例如按用户、月份或分片拆开。
- 用
OBJECT ENCODING和MEMORY USAGE抽样确认是否还留在 listpack。
收束:一句判断
Hash 省内存的前提,是你让它保持小而扁平。
关于十三Tech
我是十三,All in AI Agent 方向的架构师,专注 AI 工程实践。
我相信 AI 是程序员的最佳搭档,也希望帮助每一位开发者更好地驾驭 AI。
如果你想继续跟完这套「图解 Redis」,欢迎关注公众号 「十三Tech」。后续会继续按数据结构、底层机制、持久化、高可用和实战排查这条线更新。

