简介
在 Redis 中,哈希(Hash)类型的值本身又是一个键值对结构。简单来说,就是 key = "key",value = { { field1, value1 }, …, {fieldN, valueN} }。为了与普通的 String 类型区分,Hash 内部采用 field-value 的映射关系。

核心命令速览
hset 与 hget
hset 用于设置 hash 中指定字段的值。如果字段已存在,则覆盖;不存在则新建。
hset key field value [field value ...]
插入一组 field 的时间复杂度为 O(1),插入 N 组则为 O(N)。返回值是添加或更新的字段个数。修改字段时效果相同,无需担心重复创建的问题。

hget 则是获取指定字段的值,语法简单:
hget key field
时间复杂度 O(1),若字段不存在返回 nil。注意这里参数名是 field 而非 filed,文档中偶尔出现的拼写错误需留意。

字段检查与删除
判断字段是否存在用 hexists,返回 1 表示存在,0 表示不存在。
hexists key field
删除字段使用 hdel,支持批量删除多个字段,返回实际删除的数量。
hdel key field [field ...]

遍历与统计
获取所有字段用 hkeys,获取所有值用 hvals,两者时间复杂度均为 O(N),N 为 field 个数。如果需要一次性获取所有字段和值,hgetall 是最直接的选择。
hgetall key
批量获取多个字段的值推荐使用 hmget,比多次调用 hget 更高效。
hmget key field [field ...]
统计字段数量用 hlen,这是一个 O(1) 操作,非常适合快速检查数据规模。

特殊操作
hsetnx 仅在字段不存在时设置值,常用于防止覆盖已有数据的场景,返回 1 表示成功,0 表示失败。
对于数值型字段,hincrby 支持整数自增,hincrbyfloat 支持浮点数自增,两者都是原子操作,适合做计数器或累加器。
hincrby key field increment
hincrbyfloat key field increment
为了方便查阅,这里整理了一份常用命令的性能特征表:
| 命令 | 执行效果 | 时间复杂度 |
|---|---|---|
| hset key field value | 设置值 | O(1) |
| hget key field | 获取值 | O(1) |
| hdel key field | ||
| hlen key | 计算 field 个数 | O(1) |
| hgetall key | 获取所有的 field-value | O(k), k 是 field 个数 |
| hmget key field | ||
| hexists key field | 判断 field 是否存在 | O(1) |
| hkeys key | 获取所有的 field | O(k), k 是 field 个数 |
| hvals key | 获取所有的 value | O(k), k 是 field 个数 |
| hsetnx key field value | 设置值,但必须在 field 不存在时才能设置成功 | O(1) |
| hincrby key field n | 对应 field-value +n | O(1) |
| hincrbyfloat key field n | 对应 field-value +n | O(1) |
| hstrlen key field | 计算 value 的字符串长度 | O(1) |
内部编码机制
理解 Redis Hash 的内部编码对性能调优很有帮助。主要有两种实现方式:
- ziplist(压缩列表):当哈希元素个数小于配置项
hash-max-ziplist-entries(默认 512),且所有值都小于hash-max-ziplist-value(默认 64 字节)时,Redis 会使用 ziplist。这种结构更紧凑,内存占用更低。 - hashtable(哈希表):一旦数据量超过上述阈值,或者写入导致结构变化无法维持 ziplist 特性,Redis 会自动切换到 hashtable。虽然内存开销稍大,但读写效率稳定在 O(1)。

这意味着在处理小对象时内存更省,而处理大对象时查询更快,系统会自动平衡这两者。
应用场景
作为缓存
当需要存储结构化数据(如用户信息、商品详情)时,Hash 类型比 String 序列化更合适。它允许你只更新某个字段而不必重新序列化整个对象,这在并发场景下能减少网络传输和锁竞争。

使用 Hash 表示结构化数据示例:



