


专栏:Redis 修行录
个人主页:手握风云
目录
[一、Hash 类型](#一、Hash 类型)
[1.1. 基本概念与特点](#1.1. 基本概念与特点)
[1.2. HSET 和 HGET](#1.2. HSET 和 HGET)
[1.3. HEXISTS 和 HDEL](#1.3. HEXISTS 和 HDEL)
[1.4. HKEYS 和 HVALS](#1.4. HKEYS 和 HVALS)
[1.5. HGETALL 和 HMGET](#1.5. HGETALL 和 HMGET)
[1.6. HLEN 和 HSETNX](#1.6. HLEN 和 HSETNX)
[1.7. HINCRBY 和 HINCRBYFLOAT](#1.7. HINCRBY 和 HINCRBYFLOAT)
[1.8. Hash 内部编码机制](#1.8. Hash 内部编码机制)
[1.9. 典型使用场景](#1.9. 典型使用场景)
一、Hash 类型
1.1. 基本概念与特点
哈希类型(Hash)主要用于存储对象,其记录的类型被建模为一组组的字段与值的集合,非常类似于 Java 中的 HashMap、Python 中的字典(dictionary)。在 Redis 中,哈希类型意味着它的值(value)本身也是一个键值对结构,形如 key = "{{field1, value1}, ..., {fieldN, valueN}}"。为了与 Redis 整体的键值对(key-value)相区分,哈希类型内部的映射关系通常被称为 field-value(字段-值),内部的 value 指的是 field 所对应的值。
关系型数据库是完全结构化的,一旦增加列,所有行都会受影响;而 Redis 哈希是稀疏的,每个不同的键可以包含完全不同的 field。不过,Redis 哈希无法像关系型数据库那样进行复杂的联表查询或聚合查询。
1.2. HSET 和 HGET
HSET 与 HGET 是 Redis 中用于操作哈希(Hash)数据结构的两个基础命令,它们分别用于设置和获取哈希表中的字段值。
bash
HSET key field value [field value ...]
HSET 用于创建或修改哈希表(Hash)中一个或多个字段(field)的值。如果指定的字段已经存在于哈希中,该命令会覆盖其原有的值;如果指定的键(key)不存在,Redis 会自动创建一个新键来保存这个哈希表,返回值为被成功添加的新字段的数量(整数类型)。
从 Redis 4.0.0 版本开始,HSET 支持在一条命令中传入多个字段和值的组合。对于每个添加的字段/值对,时间复杂度为 O(1)。当命令包含 N 个字段/值对时,总的时间复杂度为 O(N)。
bash
HGET key field
HGET 命令用于获取存储在指定哈希键中某个特定字段的值,时间复杂度为 O(1)。返回值为该字段关联的值。如果指定的字段在哈希中不存在,或者指定的键(key)根本不存在,则返回 nil(或 null)。
bash
HSET KEY F1 111
HSET KEY F2 222 F3 333 F4 444
HGET KEY F1
HGET KEY F3

1.3. HEXISTS 和 HDEL
bash
HEXISTS key field
HEXISTS 命令主要用于判断 Redis 哈希(Hash)数据结构中是否存在某个指定的字段(field)。该命令的执行的时间复杂度为 O(1),运行非常高效。在执行该命令后,如果指定的字段确实存在于对应的哈希表中,命令会返回整数 1;如果该字段不存在,或者指定的键(key)本身就不存在,则会返回整数 0。
bash
HDEL key field [field ...]
HDEL 命令则主要用于从哈希表中删除一个或多个指定的字段及其对应的值。该命令允许开发者在一条命令中传入多个欲删除的字段名。在执行过程中,如果给定的字段在哈希中并不存在,Redis 会直接将其忽略;而如果哈希表中的所有字段都被删除殆尽,Redis 会自动将这个空哈希键也一并删除。该命令的时间复杂度为 O(N),这里的 N 代表即将被移除的字段数量(如果仅删除单一字段,时间复杂度则为 O(1))。命令执行完毕后,会返回实际被成功删除的字段总数。
bash
HEXISTS KEY F1
HEXISTS KEY F10
HDEL KEY F1 F2

1.4. HKEYS 和 HVALS
bash
HKEYS key
HKEYS 命令主要用于获取 Redis 哈希(Hash)数据结构中所有的字段名(field)。该命令的执行的时间复杂度为 O(N),其中 N 代表哈希表中字段的个数。在执行该命令后,Redis 会返回该哈希包含的所有字段列表;如果指定的键(key)不存在,则会返回一个空列表。此命令自 Redis 2.0.0 版本起便已提供,由于其时间复杂度可能随哈希表大小而线性增长,因此在访问控制列表(ACL)中被划归为 @read、@hash 和 @slow(慢速)类别。
bash
HVALS key
HVALS 命令则主要用于获取哈希表中的所有字段对应的值(value)。其基本语法是 HVALS key,同样要求传入指定的键名。与 HKEYS 类似,该命令的时间复杂度也是 O(N),N 同样代表哈希表中字段的数量。在命令执行完毕后,它会返回该哈希内所有的值列表;如果目标键不存在,则会返回一个空列表。该命令也是自 Redis 2.0.0 版本起可用,并且在 ACL 权限分类中与 HKEYS 一致,属于 @read、@hash 和 @slow 类别。由于这两个命令在处理庞大哈希表时可能会造成阻塞,因此在使用时需要根据数据大小合理评估性能影响。
bash
HSET key f1 111 f2 222 f3 333 f4 444
HKEYS key
HVALS key

1.5. HGETALL 和 HMGET
bash
HGETALL key
GETALL 命令主要用于获取 Redis 哈希(Hash)数据结构中所有的字段(field)及其对应的值(value)。该命令执行的时间复杂度为 O(N),其中 N 代表该哈希表中包含的字段总数。在执行命令后,Redis 会返回该哈希内所有的字段名和对应值交替出现的列表;如果指定的键(key)不存在,则会返回一个空列表或空映射。需要特别注意的是,当哈希表中的元素非常多时,直接执行 HGETALL 存在阻塞 Redis 的风险;因此,如果只需获取部分字段,建议使用 HMGET 命令,而如果必须获取全部数据,则推荐使用 HSCAN 命令进行渐进式遍历。
bash
HMGET key field [field ...]
HMGET 命令则主要用于一次性从哈希表中获取一个或多个指定字段对应的值。允许开发者在一条命令中同时传入多个需要查询的字段名。该命令的时间复杂度同样为 O(N),但此处的 N 代表的是请求查询的字段数量(若仅查询单一字段,时间复杂度则为 O(1))。命令执行完毕后,会严格按照请求中字段的排列顺序返回对应的值列表;如果某个给定的字段在哈希中并不存在,或者指定的键本身就不存在(Redis 会将其视为空哈希),那么在返回列表中对应位置的值将为 nil。
bash
HGETALL key
HMGET key f1 f4 f3

1.6. HLEN 和 HSETNX
bash
HLEN key
HLEN 命令主要用于获取 Redis 哈希(Hash)表中包含的字段(field)的数量。其执行的时间复杂度为 O(1),运行十分高效。在命令执行后,如果键存在,则返回哈希表中的字段个数;如果指定的键不存在,则返回整数 0。
bash
HSETNX key field value
HSETNX 命令用于在指定字段不存在的情况下,为哈希表中的该字段设置值。其时间复杂度同样为 O(1)。在执行时,如果目标键不存在,Redis 会自动创建一个新的空哈希并执行设置;如果该字段已经存在,则命令不会产生任何效果。执行完毕后,如果成功设置了新字段,会返回整数 1;若由于字段已存在而未作任何操作,则返回整数 0。
bash
HLEN key
HSETNX key f1 100
HSETNX key1 f1 100
HGET key1 f1

1.7. HINCRBY 和 HINCRBYFLOAT
bash
HINCRBY key field increment
HINCRBY 命令主要用于将哈希表中指定字段所对应的数值,加上一个指定的整数增量。其时间复杂度为 O(1)。如果目标键或字段不存在,Redis 会在执行加法操作前将该字段的值初始化为 0。增量值可以是正数也可以是负数(即执行减法操作),但支持的数值被限制在 64 位有符号整数的范围内。操作完成后,命令会返回该字段在进行加减计算之后的新值。
bash
HINCRBYFLOAT key field increment
HINCRBYFLOAT 命令则是 HINCRBY 的浮点数版本,用于将哈希表指定字段的值加上一个双精度浮点数增量。其时间复杂度为 O(1)。与 HINCRBY 类似,如果键或字段不存在,其初始值会被视为 0;传入负的增量则代表减去对应的值。该命令的一个特点是允许使用科学计数法来表示浮点数。不过,如果字段当前的内容或传入的增量无法被解析为双精度浮点数,或者键的数据类型不是哈希,命令将会报错。执行成功后,它会返回修改后的最新值。
bash
HINCRBY key1 f1 10
HGET key1 f1
HINCRBY key1 f1 -3
HGET key1 f1

1.8. Hash 内部编码机制
为了在内存占用和执行效率之间取得平衡,Redis 的哈希类型拥有两种底层内部编码实现:
- ziplist(压缩列表):当哈希类型的元素个数小于 hash-max-ziplist-entries 配置项(默认 512 个),并且所有 value 的大小都小于 hash-max-ziplist-value 配置项(默认 64 字节)时,Redis 会优先使用 ziplist。ziplist 使用极其紧凑的结构来实现连续存储,能极大地节省内存消耗。
- hashtable(哈希表):当哈希类型无法满足上述 ziplist 的条件时,若继续使用压缩列表会导致读写效率显著下降,Redis 就会将底层结构自动转换为 hashtable。hashtable 能够保证在数据量大时依然维持 O(1) 的读写时间复杂度。
1.9. 典型使用场景
由于哈希类型的结构特点,它非常适合用来存储对象信息(如用户属性、商品详情等)。在实际开发中,一般将对象的唯一标识(如 uid)作为 Redis 的 key,对象的各个属性名作为 field,属性值作为 value。相比于其他缓存对象的方式,哈希类型有其独特的应用优势:
-
对比原生字符串类型(每个属性分配一个独立的 key): 原生字符串虽然操作简单灵活,但会占用过多的键,导致内存开销极大,且属于同一个对象的信息在 Redis 中过于分散,缺乏内聚性,基本不具备大规模实用价值。
-
对比序列化字符串类型(如将对象转为 JSON 字符串存储): JSON 格式很适合将对象作为一个整体进行存取,且内存使用率较高。但缺点在于存在序列化和反序列化的性能开销,同时如果业务中经常只需要修改或读取某个别属性(例如只更新用户的年龄),JSON 的方式就显得非常僵化和低效。
-
哈希类型自身的优势与局限: 使用 Hash 缓存对象的优点是简单、直观且极其灵活,针对局部属性的变更和读取非常高效。劣势在于,开发人员需要留意把控单 key 的数据量,防止其超出阈值后由 ziplist 转换为 hashtable 从而引发较大的内存消耗突增。
最后需要注意的是,Redis 的哈希类型具有稀疏性。在关系型数据库中添加一列,所有行都会带有该结构(即使值为 null);但在 Redis 哈希中,不同的 key 可以拥有完全不同的 field 集合。这种非结构化特征虽然灵活,但也意味着无法像关系数据库那样直接利用它去执行诸如联表、聚合分析等复杂的查询操作。