Skip to content

Redis 的 hash 是什么? ​

  • 标签:后端、Redis

🏛 业务架构师 ​

Redis Hash 是字段级 KV 结构:一个 key 下挂多个 field→value,支持单字段的读写(hget/hset)、增减(hincrby)、部分获取(hmget)。

它回答的业务问题是:当对象的更新粒度是"字段"而不是"整个对象"时。对比两种存法就清楚:

  • String 存整个 JSON:改一个字段要 GET 全量→改→SET 全量,并发改不同字段还会互相覆盖(丢失更新);读一个字段也要拉全量,网络与反序列化都是浪费;
  • Hash 分字段存:hset 购物车 商品1001 2 只动一个字段;hgetcart 只取数量——粒度对齐业务操作,就省了全量搬运的成本。

我的典型落地场景:

  • 购物车:key=用户 ID,field=商品 SKU,value=数量。加购/改数量/删单品都是字段级操作,天然无覆盖冲突;
  • 商品详情缓存:标题、价格、库存分开字段——价格高频变、标题基本不变,拆开后价格更新不触碰其他字段,配合 binlog 订阅精准失效更顺;
  • 用户画像/会话存储:按需取部分属性,节省大对象传输;
  • 每日指标看板:field=小时/维度,局部更新。

边界提醒:小 hash 在 Redis 内部自动紧凑编码(listpack),省内存且高效;但字段数失控会退化——超大 hash(十万 field)要评估拆分(按 field 前缀拆多个 key)。设计时给"一个 hash 最多多少字段"定规范(经验值 1000 内)。

一句话:Hash 的架构意义是"更新粒度与业务操作粒度对齐"——粒度对齐了,网络、并发、内存三项成本同时下降。

🏗 系统架构师 ​

Redis Hash 是string 类型的 field-value 映射表,适合"一个对象下多个字段独立读写"的场景:

  • 与 String 存整对象 JSON 对比:String 整存整取(改一个字段要整读反序列化→改→写回,网络放大、并发覆盖);Hash 支持 HSET 单字段更新、HGET 单字段读取,字段级操作原子且省流量;
  • 内存优化:小 Hash(field/ value 都短、条数少)底层用 **listpack(7.0 前 ziplist)**紧凑存储,省掉大量指针开销;超阈值转 hashtable(hash-max-listpack-entries 默认 128、value 64 字节);
  • 大 Hash 风险:field 数十万后变 big key,HGETALL 全量拉取会阻塞——我规范里限制单 Hash field 数(超过就按 field 再哈希拆成 user:1:basic / user:1:ext,或按 field 分片到多个 key),只 HGETALL 小对象,大对象按需 HMGET 指定字段。

典型落地:购物车(key=cart:userId,field=商品ID,value=数量,HINCRBY 增减)、用户资料缓存、商品多维属性、AB 实验标记(field=实验,value=分组)。

选型判断:字段级并发修改、部分读、字段数可控 → Hash;整体读写且并发低 → String+JSON 更简单序列化成本低。

💻 全栈开发专家 ​

Hash = field-value 映射的组,适合"一个对象的部分字段频繁读写"的场景。

bash
HSET cart:1001 sku:88 2 sku:95 1    # 购物车:用户1001 加两个商品
HINCRBY cart:1001 sku:88 1          # 数量+1,原子
HGETALL cart:1001                   # 结算时取全量
HDEL cart:1001 sku:95               # 移除
EXPIRE cart:1001 604800             # 购物车7天过期

为什么不用 String 存整个 JSON:改一个字段要 GET 全量 → 反序列化 → 修改 → 序列化 → SET 回去,大对象高频更新又费网络又非原子。Hash 天然字段级操作 + HINCRBY 原子计数。

典型场景:购物车、用户资料分散更新、商品多维度计数(库存/浏览/点赞分别一个 field)、分布式会话存储。

两个工程坑:

  1. 大 Hash:几十万元素后 HGETALL 卡主线程(bigkey)。按业务拆:cart:1001 → 按类目拆 cart:1001:clothes;读用 HMGET 指定 field,别全量;必要时 HSCAN 游标遍历;
  2. 编码选择:小 hash 用 ziplist/listpack 省内存,超过 hash-max-ziplist-entries(128)/value-length(64) 转标准 hashtable,内存跳涨——监控大 hash 的字段数,超限前主动拆。