目录
- Hash类型介绍
- 特性
- hash 的内部编码方式/底层结构
- hashtable
- ziplist
- listpack
- 适用场景
- 举例
- 常用命令
- hset
- 示例
- hsetnx
- 示例:
- hmset
- 示例
- hget
- 示例
- hmget
- 示例
- hgetall
- 示例
- hdel
- 示例
- hlen
- 示例
- hexists
- 示例
- hincrby
- 示例
- hincrbyfloat
- 示例
- hkeys
- 示例
- hvals
- 示例
Hash类型介绍
Hash 类型是一种键值对集合,这种数据类型适合用于存储对象
特性
- 键值对集合:Hash 类型可以存储多个键值对,每个键都有一个对应的值。
- 二进制安全:Hash 类型的键和值都是二进制安全的,这意味着它们可以包含任何数据,包括二进制数据。
- 大容量:单个 Hash 类型可以存储超过 4 亿个键值对。
- 高效的查找速度:无论 Hash 中存储了多少数据,查找某个键的速度都非常快。
hash 的内部编码方式/底层结构
Redis 的 Hash 类型会根据实际情况在压缩列表(ziplist)和散列表(hashtable)之间进行切换,这主要取决于两个配置参数:hash-max-ziplist-entries 和 hash-max-ziplist-value。
- hash-max-ziplist-entries:这个参数用于设置压缩列表可以存储的最大节点数量。如果一个 Hash 类型的元素数量超过这个值,那么就会从压缩列表切换到散列表。默认值为 512;
- hash-max-ziplist-value:这个参数用于设置压缩列表中每个节点的最大值大小(以字节为单位)。如果一个 Hash 类型的任何元素的大小超过这个值,那么就会从压缩列表切换到散列表。默认值为 64。
hashtable
- 最基本的哈希表(不是 java 标准库中的 HashTable),redis 内部对哈希表的实现方式和 java 中的哈希表可能不太一样,但是整体思想都是一样的;时间上复杂度O(1),但是空间上会有一定的浪费(hash 是一个数组,数组上有些位置有元素,有些位置没有元素).
- 当 Hash 类型存储的字段和值的数量较多,或者字段和值的字符串长度较长时,Redis 会选择使用散列表作为底层实现。散列表是一种常见的键值对映射结构,它通过一个散列函数将键映射到一个桶中,然后在桶中进行查找。这种方式的优点是查找和修改数据的性能较高,但是占用的内存也较多。
- Redis 的散列表(hash table)是一种常见的键值对映射结构,它通过一个散列函数将键映射到一个桶中,然后在桶中进行查找。Redis 的散列表使用链表法解决哈希冲突,即当多个键映射到同一个桶时,将它们存储在同一个链表中
ziplist
压缩列表,当哈希表里的元素比较少的时候,就优化成了 ziplist 了,在Redis7.0之前使用,特点是内存占用教少,能够节省空间,但是读写元素的速度比较慢;
- 如果 hash 中的元素比较少,使用 ziplist 表示. 元素比较多的时候使用 hashtable 表示.
- 每个 value 值的长度都比较短的,使用 ziplist 表示. 如果某个 value 长度太长,也会转化成 hashtable.
- 压缩的本质就是对数据进行重新编码,不同的数据有不同的特点,结合这些特点,重新编码后,就能够缩小体积
- 例如 abbcccdddd 重新编码就变成了 1a2b3c4d.
listpack
紧凑列表,在Redis7.0版本之后取代了ziplist,同样也是hash到达一定阀值后转换为hashtable。
适用场景
- 存储对象:Hash 类型可以存储多个键值对,非常适合用于存储对象。例如,你可以使用 Hash 类型存储用户的信息,如用户名、密码、邮箱等;
- 数据分析:你可以使用 Hash 类型存储各种统计数据,例如用户的行为数据,然后进行数据分析;
- 社交网络:在社交网络应用中,你可以使用 Hash 类型存储用户的朋友列表、粉丝列表等
- 购物车场景的使用。使用用户ID作为key,商品ID作为field,商品数量或规格等信息作为value,便于快速增删改查购物车中的商品项
- 缓存配置信息:应用程序的配置信息,如各种设置参数,可以集中存储在Hash中,方便统一管理和实时更新
- 权限列表管理: 用Hash存储用户的权限信息,key为用户ID,field表示权限标识符,value为权限状态(如允许/禁止)。
- 进度存储:如小说阅读进度、视频播放进度等场景,key可以是用户ID+资源ID组合,field为资源唯一标识,value为对应的进度值。
- 关系模型数据建模:当需要在Redis中模拟关系型数据库中的一对多或者多对多关系时,Hash可以作为一个轻量级的解决方案,简化数据结构设计
- 计数器集合:当需要统计一组相关联的数据项的不同维度计数时,可以使用Hash存储每个维度的计数值。
举例
常用命令
命令 | 描述 |
---|---|
hset key field value | filed的不存在新增并返回1,如果filed已经存在就覆盖更新并返回0 |
hsetnx key field value | 如果filed不存在则设置值为字符串value并返回1,否则不做任何操作返回0 |
hgetall key field | 获取这个key的所有field-value,返回结果为数组 |
hdel key field | 删除field的value,删除成功返回1,删除失败返回0 |
hdel key | 删除key的所有field-value,删除成功返回1,删除失败返回0 |
hmset k1 f1 v1 k2 f2 v2 … | 批量操作一次性新增或更新多个field |
hmget f1 f2 … | 批量操作一次性获取多个field的value |
hexists key field | 判断field是否存在,存在返回1,否侧返回0 |
hsetnx key field value | field不存在则设置值,否则不操作 |
hvals key | 获取hash结构的所有值 |
hset
- 语法:hset key field value
- 解释:
- 将哈希表 key 中的域 field 的值设为 value 。
- 如果 key 不存在,一个新的哈希表被创建并进行 HSET 操作。
- 如果域 field 已经存在于哈希表中,旧值将被覆盖
- 时间复杂度:O(1)
- 返回值:
- 如果 field 是哈希表中的一个新建域,并且值设置成功,返回 1 。
- 如果哈希表中域 field 已经存在且旧值已被新值覆盖,返回 0 。
示例
127.0.0.1:6379[3]> hset mywebsite csdn "zhoujl.blog.csdn.net"
(integer) 1
127.0.0.1:6379[3]> hset mywebsite csdn "https://zhoujl.blog.csdn.net"
(integer) 0
127.0.0.1:6379[3]>
hsetnx
- 语法:hsetnx key field value
- 解释:
- 将哈希表 key 中的域 field 的值设置为 value ,当且仅当域 field 不存在。
- 若域 field 已经存在,该操作无效。
- 如果 key 不存在,一个新哈希表被创建并执行 HSETNX 命令。
- 时间复杂度:O(1)
- 返回值:
- 设置成功,返回 1 。
- 如果给定域已经存在且没有操作被执行,返回 0 。
示例:
127.0.0.1:6379[3]> hsetnx user username admin
(integer) 1
127.0.0.1:6379[3]> hsetnx user userage 20
(integer) 1
127.0.0.1:6379[3]> hsetnx user username root
(integer) 0
127.0.0.1:6379[3]>
hmset
- 语法:hmset key field value [field value …]
- 解释:
- 同时将多个 field-value (域-值)对设置到哈希表 key 中。
- 此命令会覆盖哈希表中已存在的域。
- 如果 key 不存在,一个空哈希表被创建并执行 HMSET 操作。
- 复杂度:O(N), N 为 field-value 对的数量。
- 返回值类型:
- 如果命令执行成功,返回 OK 。
- 当 key 不是哈希表(hash)类型时,返回一个错误。
示例
127.0.0.1:6379[3]> hmset role roleId 1 roleName admin
OK
127.0.0.1:6379[3]> hget role roleId
"1"
127.0.0.1:6379[3]> hget role roleName
"admin"
127.0.0.1:6379[3]>
hget
- 语法:hget key field
- 解释:返回哈希表 key 中给定域 field 的值
- 时间复杂度:O(1)
- 返回值:
- 给定域的值。
- 当给定域不存在或是给定 key 不存在时,返回 nil 。
示例
127.0.0.1:6379[3]> hset site redis redis.com
(integer) 1
127.0.0.1:6379[3]> hget site redis
"redis.com"
127.0.0.1:6379[3]> hget site mysql
(nil)
127.0.0.1:6379[3]>
hmget
- 语法:hmget key field [field …]
- 解释:
- 返回哈希表 key 中,一个或多个给定域的值。
- 如果给定的域不存在于哈希表,那么返回一个 nil 值。
- 因为不存在的 key 被当作一个空哈希表来处理,所以对一个不存在的 key 进行 HMGET 操作将返回一个只带有 nil 值的表。
- 时间复杂度:O(N), N 为给定域的数量。
- 返回值:一个包含多个给定域的关联值的表,表值的排列顺序和给定域参数的请求顺序一样
示例
127.0.0.1:6379[3]> hmset pet dog anpei cat mimi
OK
127.0.0.1:6379[3]> hmget pet dog cat penguin
1) "anpei"
2) "mimi"
3) (nil)
127.0.0.1:6379[3]>
hgetall
- 语法:hgetall key
- 解释
- 返回哈希表 key 中,所有的域和值。
- 在返回值里,紧跟每个域名(field name)之后是域的值(value),所以返回值的长度是哈希表大小的两倍。
- 时间复杂度:O(N), N 为哈希表的大小。
- 返回值:
- 以列表形式返回哈希表的域和域的值。
- 若 key 不存在,返回空列表。
示例
127.0.0.1:6379[3]> hset city zz zhengzhou
(integer) 1
127.0.0.1:6379[3]> hset city hz hangzhou
(integer) 1
127.0.0.1:6379[3]> hgetall city
1) "zz" # 属性/域
2) "zhengzhou" # 值
3) "hz"
4) "hangzhou"
127.0.0.1:6379[3]>
hdel
- 语法:hdel key field [field …]
- 解释:
- 删除哈希表 key 中的一个或多个指定域,不存在的域将被忽略。
- 注:在 Redis2.4 以下的版本里, HDEL 每次只能删除单个域,如果你需要在一个原子时间内删除多个域,请将命令包含在 MULTI / EXEC 块内。
- 时间复杂度:O(N), N 为要删除的域的数量。
- 返回值:被成功移除的域的数量,不包括被忽略的域。
示例
127.0.0.1:6379[3]> hgetall city1) "zz"2) "zhengzhou"3) "hz"4) "hangzhou"5) "kf"6) "kaifeng"7) "ly"8) "luoyang"9) "bj"
10) "beijing"
11) "sh"
12) "shanghai"
127.0.0.1:6379[3]> hdel city sh
(integer) 1
127.0.0.1:6379[3]> hdel city hz bj
(integer) 2
127.0.0.1:6379[3]> hgetall city
1) "zz"
2) "zhengzhou"
3) "kf"
4) "kaifeng"
5) "ly"
6) "luoyang"
127.0.0.1:6379[3]>
hlen
- 语法:hlen key
- 解释:返回哈希表 key 中域的数量。
- 时间复杂度:O(1)
- 返回值:
- 哈希表中域的数量。
- 当 key 不存在时,返回 0 。
示例
127.0.0.1:6379[3]> hgetall city
1) "zz"
2) "zhengzhou"
3) "kf"
4) "kaifeng"
5) "ly"
6) "luoyang"
127.0.0.1:6379[3]> hlen city
(integer) 3
127.0.0.1:6379[3]>
hexists
- 语法:hexists key field
- 解释:查看哈希表 key 中,给定域 field 是否存在。
- 时间复杂度:O(1)
- 返回值:
- 如果哈希表含有给定域,返回 1 。
- 如果哈希表不含有给定域,或 key 不存在,返回 0 。
示例
127.0.0.1:6379[3]> hexists city sh
(integer) 0
127.0.0.1:6379[3]> hexists city zz
(integer) 1
127.0.0.1:6379[3]>
hincrby
- 语法:hincrby key field increment
- 解释:
- 为哈希表 key 中的域 field 的值加上增量 increment 。
- 增量也可以为负数,相当于对给定域进行减法操作。
- 如果 key 不存在,一个新的哈希表被创建并执行 HINCRBY 命令。
- 如果域 field 不存在,那么在执行命令前,域的值被初始化为 0 。
- 对一个储存字符串值的域 field 执行 HINCRBY 命令将造成一个错误。
- 本操作的值被限制在 64 位(bit)有符号数字表示之内
- 时间复杂度:O(1)
- 返回值:执行 HINCRBY 命令之后,哈希表 key 中域 field 的值。
示例
# increment 为正数
127.0.0.1:6379[3]> HEXISTS counter page_view # 对空域进行设置
(integer) 0
127.0.0.1:6379[3]> HINCRBY counter page_view 200
(integer) 200
127.0.0.1:6379[3]> HGET counter page_view
"200"
# increment 为负数
127.0.0.1:6379[3]> HGET counter page_view
"200"
127.0.0.1:6379[3]> HINCRBY counter page_view -50
(integer) 150
127.0.0.1:6379[3]> HGET counter page_view
"150"
# 尝试对字符串值的域执行 HINCRBY 命令
127.0.0.1:6379[3]> HSET myhash string hello,world # 设定一个字符串值
(integer) 1
127.0.0.1:6379[3]> HGET myhash string
"hello,world"
127.0.0.1:6379[3]> HINCRBY myhash string 1 # 命令执行失败,错误。
(error) ERR hash value is not an integer
127.0.0.1:6379[3]> HGET myhash string # 原值不变
"hello,world"
hincrbyfloat
- 语法:hincrbyfloat key field increment
- 解释:
- 为哈希表 key 中的域 field 加上浮点数增量 increment 。
- 如果哈希表中没有域 field ,那么 HINCRBYFLOAT 会先将域 field 的值设为 0 ,然后再执行加法操作。
- 如果键 key 不存在,那么 HINCRBYFLOAT 会先创建一个哈希表,再创建域 field ,最后再执行加法操作。
- 当以下任意一个条件发生时,返回一个错误:
- 域 field 的值不是字符串类型(因为 redis 中的数字和浮点数都以字符串的形式保存,所以它们都属于字符串类型)
- 域 field 当前的值或给定的增量 increment 不能解释(parse)为双精度浮点数(double precision floating point number)
- HINCRBYFLOAT 命令的详细功能和 INCRBYFLOAT 命令类似,请查看 INCRBYFLOAT 命令获取更多相关信息。
- 时间复杂度:O(1)
- 返回值:执行加法操作之后 field 域的值。
示例
# 值和增量都是普通小数
127.0.0.1:6379[3]> HSET mykey field 10.50
(integer) 1
127.0.0.1:6379[3]> HINCRBYFLOAT mykey field 0.1
"10.6"
# 值和增量都是指数符号
127.0.0.1:6379[3]> HSET mykey field 5.0e3
(integer) 0
127.0.0.1:6379[3]> HINCRBYFLOAT mykey field 2.0e2
"5200"
# 对不存在的键执行 HINCRBYFLOAT
127.0.0.1:6379[3]> EXISTS price
(integer) 0
127.0.0.1:6379[3]> HINCRBYFLOAT price milk 3.5
"3.5"
127.0.0.1:6379[3]> HGETALL price
1) "milk"
2) "3.5"
# 对不存在的域进行 HINCRBYFLOAT
127.0.0.1:6379[3]> HGETALL price
1) "milk"
2) "3.5"
127.0.0.1:6379[3]> HINCRBYFLOAT price coffee 4.5 # 新增 coffee 域
"4.5"
127.0.0.1:6379[3]> HGETALL price
1) "milk"
2) "3.5"
3) "coffee"
4) "4.5
hkeys
- 语法:hkeys key
- 解释:返回哈希表 key 中的所有域。
- 时间复杂度:O(N), N 为哈希表的大小。
- 返回值:
- 一个包含哈希表中所有域的表。
- 当 key 不存在时,返回一个空表。
示例
# 哈希表非空
127.0.0.1:6379[3]> HMSET website google www.google.com yahoo www.yahoo.com
OK
127.0.0.1:6379[3]> HKEYS website
1) "google"
2) "yahoo"
# 空哈希表/key 不存在
127.0.0.1:6379[3]> EXISTS fake_key
(integer) 0
127.0.0.1:6379[3]> HKEYS fake_key
(empty list or set)
hvals
- 语法:hvals key
- 解释:返回哈希表 key 中所有域的值。
- 时间复杂度:O(N), N 为哈希表的大小。
- 返回值:
- 一个包含哈希表中所有值的表。
- 当 key 不存在时,返回一个空表。
示例
# 非空哈希表
127.0.0.1:6379[3]> HMSET website google www.google.com yahoo www.yahoo.com
OK
127.0.0.1:6379[3]> HVALS website
1) "www.google.com"
2) "www.yahoo.com"
# 空哈希表/不存在的 key
127.0.0.1:6379[3]> EXISTS not_exists
(integer) 0
127.0.0.1:6379[3]> HVALS not_exists
(empty list or set)