Redis:hash类型
- hash命令
- 设置与读取
- HSET
- HGET
- HMGET
- HSETNX
- 哈希操作
- HEXISTS
- HDEL
- HKEYS
- HVALS
- HGETALL
- HLEN
- HINCRBY
- HINCRBYFLOAT
- 内部编码
- ziplist
- hashtable
目前主流的编程语言中,几乎都提供了哈希表相关的容器,Redis
自然也会支持对应的内容,满足程序员的需求。
如果说要存储一个用户的姓名和年龄的映射关系,只使用string
类型的话,就是下图的样子:
此时Redis
的key
放对应的名称,value
放对应的值。但是要注意的是,为了区分不同用户,要加上一些前缀来对key
命名,比如user1:name
,user2:name
,这会比较麻烦。
如果支持了哈希结构:
此时value
内部存储的是哈希表结构,相当于嵌套了两层映射关系。为了不搞混Redis
本身的key
和哈希表内部的key
,所以哈希内部的键称为field
。
hash命令
设置与读取
HSET
- 设置
hash
中指定字段field
和值value
hset key field value [field value ...]
返回值是设置成功的filed - value
键值对的个数。
示例:
第一次创建了一个哈希表hash1
,设置了两个键值对f1 - 111
、f2 - 222
,返回2
。第二次设置同一张哈希表,由于f1
和f2
已经存在了,只设置成功了f3
,所以返回1
。
HGET
- 获取
hash
中指定filed
的值
hget key field
返回对应字段的value
,如果key
或者field
不存在,返回nil
。
示例:
HMGET
- 一次获取
hash
中多个字段的值
hget key field [field ...]
返回所有field
对应的value
,如果key
或者field
不存在,返回nil
。
示例:
示例中,name
和age
都是存在的字段,返回了对应的值,而address
不存在,返回nil
。
另外的,还有与hmget
对应的hmset
,可以一次设置多个哈希键值对,但是hset
本身就支持设置多个哈希键值对,所以没必要。
HSETNX
- 在字段不存在的情况下,设置
hash
中的字段和值
hsetnx key field value
如果field
已经存在,那么此次设置失败,返回0
表示设置失败,返回1
表示设置成功。
第一次设置user name
失败,因为name
字段已经存在,第二次user friend
设置成功,因为原先不存在该field
。
哈希操作
HEXISTS
- 判断
hash
中是否有指定的field
hexists key field
返回0
表示不存在,返回1
表示存在。
示例:
HDEL
- 删除
hash
中的filed
字段
hdel key field [field ...]
返回本次操作删除的字段个数。
示例:
第一次删除了f3
,返回1
。第二次删除了f1
和f2
,返回2
。
如果使用del
,而不是hdel
,那么删除的是整张哈希表。
HKEYS
- 获取
hash
中的所有field
hkeys key
返回所有的field
。
示例:
HVALS
- 获取
hash
中的所有value
hvals key
返回所有的value
。
示例:
HGETALL
- 获取
hash
中所有的field
和value
hgetall key
返回所有的field
以及对应的value
。
示例:
从上往下以field_1
、value_1
、field_2
、value_2
、field_3
、value_3
的顺序输出。
HLEN
- 获取
hash
中所有字段的个数
hlen key
返回field
个数。
示例:
要注意的是,这个操作时间复杂度是O(1)
,Redis
不会去遍历哈希表,而是有专门的变量维护哈希表的大小,需要时直接读取变量即可。
HINCRBY
- 把
hash
的指定的field
对应的value
增加指定值
hincrby key field increment
因为hash
内部的value
还是一个string
,而string
可以存储整数,也就可以支持算数操作了。
与incrby
一样,支持正负数,如果不存在那么视为数字0
,最后返回变化后的值。
示例:
示例中,user
包含name
、age
、email
字段,第一次对age
自增2
。第二次自增一个不存在的键id
,此时id
默认视为0
。
HINCRBYFLOAT
- 把
hash
的指定的field
对应的value
增加指定浮点值
hincrbyfloat key field increment
与incrbyfloat
一样,支持正负数,如果不存在那么视为数字0
,最后返回变化后的值。
总结:
命令 | 执行效果 |
---|---|
hset key field value | 设置值 |
hget key field | 获取值 |
hdel key field[field...] | 删除field |
hlen key | 计算field 个数 |
hgetall key | 获取所有的field-value |
hmget field[field...] | 批量获取field-value |
hmset field value[field value...] | 批量设置field-value |
hexists key field | 判断field 是否存在 |
hkeys key | 获取所有的field |
hvals key | 获取所有的value |
hsetnx key field value | 设置值,但必须在field 不存在时才能设置成功 |
hincrby key field n | 对应field-value +n |
hincrbyfloat key field n | 对应field-value +n |
hstrlen key field | 计算value 的字符串长度 |
内部编码
hash
内部编码格式包含两种:ziplist
和hashtable
。
ziplist
压缩列表是一种内存紧凑的存储方式,适合存储数量较少且元素较小的哈希。具体来说,当hash
类型的元素个数小于 hash-max-ziplist-entries
(默认 512 个),并且所有值的长度都小于 hash-max-ziplist-value
(默认 64 字节)时,Redis
会使用 ziplist
作为哈希的内部实现。
这些配置在/etc/redis/redis.conf
内修改。
优点:
- 内存节省:
ziplist
使用连续的内存块来存储数据,这种紧凑的存储方式可以有效地减少内存碎片和开销。 - 结构简单:适合小规模数据,尤其是在内存资源有限的情况下。
缺点:
- 操作效率:随着数据量的增加,
ziplist
的读写效率会下降。尤其是在需要频繁更新的场景中,ziplist
的线性查找特性使得操作复杂度较高。 - 扩展性差:不适合大规模数据存储。
hashtable
当哈希类型无法满足 ziplist
的条件时,Redis
会自动切换到使用哈希表作为哈希的内部实现。
优点:
- 高效的读写:哈希表的读写时间复杂度为
O(1)
,即使在数据量较大时也能保证高效的访问。 - 良好的扩展性:适合存储大量数据和需要频繁更新的场景。
缺点:
- 内存占用:相较于
ziplist
,哈希表在内存使用上相对较多,特别是在存储小数据集时,内存开销更为显著。