1.hash类型的基本介绍
哈希表[之前学过的所有数据结构中,最最重要的]
1.日常开发中,出场频率非常高.
2.面试中,非常重要的考点,
Redis 自身已经是键值对结构了Redis 自身的键值对就是通过 哈希 的方式来组织的
把 key 这一层组织完成之后, 到了 value 这一层~~ value 的其中一种类型还可以再是 哈希
哈希类型中的映射关系通常称为 field-value,⽤于区分 Redis 整体的键值对(key-value),注意这⾥的 value 是指 field 对应的值,不是键(key)对应的值,请注意 value 在不同上下⽂的作⽤。
2.hash命令
2.1 hset
HSET key field value [field value ...]
命令有效版本:2.0.0 之后时间复杂度:插⼊⼀组 field 为 O(1), 插⼊ N 组 field 为 O(N)返回值:添加的字段的个数。
redis> HSET myhash field1 "Hello"(integer) 1redis> HGET myhash field1"Hello"
2.2 hget
HGET key field
命令有效版本:2.0.0 之后时间复杂度:O(1)返回值:字段对应的值或者 nil。
redis> HSET myhash field1 "foo"(integer) 1redis> HGET myhash field1"foo"redis> HGET myhash field2(nil)
2.3 HEXISTS
HEXISTS key field
命令有效版本:2.0.0 之后
时间复杂度:O(1)返回值:1 表⽰存在,0 表⽰不存在。⽰例:
redis> HSET myhash field1 "foo"
(integer) 1redis> HEXISTS myhash field1(integer) 1redis> HEXISTS myhash field2(integer) 0
2.4 HDEL
HDEL key field [field ...]
命令有效版本:2.0.0 之后时间复杂度:删除⼀个元素为 O(1). 删除 N 个元素为 O(N).返回值:本次操作删除的字段个数。
redis> HSET myhash field1 "foo"(integer) 1redis> HDEL myhash field1(integer) 1redis> HDEL myhash field2(integer) 0
- del 删除的是 key
- hdel 删除的是 field
2.5 HKEYS
获取 hash 中的所有字段。
HKEYS key
命令有效版本:2.0.0 之后时间复杂度:O(N), N 为 field 的个数.返回值:字段列表。
redis> HSET myhash field1 "Hello"
(integer) 1redis> HSET myhash field2 "World"(integer) 1redis> HKEYS myhash1) "field1"2) "field2"
2.6 HVALS
HVALS key
命令有效版本:2.0.0 之后时间复杂度:O(N), N 为 field 的个数.返回值:所有的值。
redis> HSET myhash field1 "Hello"(integer) 1redis> HSET myhash field2 "World"(integer) 1redis> HVALS myhash1) "Hello"2) "World"
2.7 HGETALL
HGETALL key
命令有效版本:2.0.0 之后时间复杂度:O(N), N 为 field 的个数.返回值:字段和对应的值。
redis> HSET myhash field1 "Hello"(integer) 1redis> HSET myhash field2 "World"(integer) 1redis> HGETALL myhash1) "field1"2) "Hello"3) "field2"4) "World"
2.8 HMGET
HMGET key field [field ...]
命令有效版本:2.0.0 之后时间复杂度:只查询⼀个元素为 O(1), 查询多个元素为 O(N), N 为查询元素个数.返回值:字段对应的值或者 nil。
redis> HSET myhash field1 "Hello"(integer) 1redis> HSET myhash field2 "World"(integer) 1redis> HMGET myhash field1 field2 nofield1) "Hello"2) "World"3) (nil)
在使⽤ HGETALL 时,如果哈希元素个数⽐较多,会存在阻塞 Redis 的可能。如果开发⼈员只 需要获取部分 field,可以使⽤ HMGET,如果⼀定要获取全部 field,可以尝试使⽤ HSCAN命令,该命令采⽤渐进式遍历哈希类型。【敲一次命令,遍历一小部分.
再敲一次,再遍历一小部分时间就是可控的~~化整为零 】
ConcurrentHashMap(线程安全的 哈希表)
这个哈希表在扩容的时候,也是按照化整为零的方式进行的!!
Java 标准库直接提供了一些线程安全的 集合类
(Java 中也有"容器"这样的术语,指的是别的了)
2.9 HLEN
HLEN key
命令有效版本:2.0.0 之后时间复杂度:O(1)返回值:字段个数。
示例
redis> HSET myhash field1 "Hello"(integer) 1redis> HSET myhash field2 "World"(integer) 1redis> HLEN myhash(integer) 2
2.10 HSETNX
HSETNX key field value
命令有效版本:2.0.0 之后时间复杂度:O(1)返回值:1 表⽰设置成功,0 表⽰失败。
redis> HSETNX myhash field "Hello"(integer) 1redis> HSETNX myhash field "World"(integer) 0redis> HGET myhash field"Hello"
2.11 HINCRBY
HINCRBY key field increment
命令有效版本:2.0.0 之后时间复杂度:O(1)返回值:该字段变化之后的值。
redis> HSET myhash field 5(integer) 1redis> HINCRBY myhash field 1(integer) 6redis> HINCRBY myhash field -1(integer) 5redis> HINCRBY myhash field -10(integer) -5
2.12 HINCRBYFLOAT
HINCRBYFLOAT key field increment
命令有效版本:2.6.0 之后时间复杂度:O(1)返回值:该字段变化之后的值。
redis> HSET mykey field 10.50(integer) 1redis> HINCRBYFLOAT mykey field 0.1"10.6"redis> HINCRBYFLOAT mykey field -5"5.6"redis> HSET mykey field 5.0e3(integer) 0redis> HINCRBYFLOAT mykey field 2.0e2"5200"
3.命令小节
命令 | 执⾏效果 | 时间复杂度 |
hset key field value | 设置值 | O(1) |
hget key field | 获取值 | O(1) |
hdel key field [field ...] | 删除 field | O(k), k 是 field个数 |
hlen key | 计算 field 个数 | O(1) |
hgetall key | 获取所有的 field-value |
O(k), k 是 field个数
|
hmget field [field ...] | 批量获取 field-value |
O(k), k 是 field个数
|
hmset field value [field
value ...]
| 批量获取 field-value |
O(k), k 是 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) |
4.hash编码方式
压缩:rar, zip, gzip,7....
一些具体的压缩算法~~
压缩的本质,是针对数据进行重新编码.
不同的数据,有不同的特点.结合这些特点,进行精妙的设计重新编码之后,就能够缩小体积~
哈希的内部编码有两种:• ziplist(压缩列表):当哈希类型元素个数⼩于 hash-max-ziplist-entries 配置(默认 512 个)、 同时所有值都⼩于 hash-max-ziplist-value 配置(默认 64 字节)时,Redis 会使⽤ ziplist 作为哈希的内部实现,ziplist 使⽤更加紧凑的结构实现多个元素的连续存储,所以在节省内存⽅⾯⽐hashtable 更加优秀。 (内部的数据结构更加精妙)• hashtable(哈希表):当哈希类型⽆法满⾜ ziplist 的条件时,Redis 会使⽤ hashtable 作为哈希的内部实现,因为此时 ziplist 的读写效率会下降,⽽ hashtable 的读写时间复杂度为 O(1)。
ziplist 也是同理~~
内部的数据结构也是精心设计的~~
【目的节省内存空间.】
表示一个普通的hash表,可能会浪费一定的空间~~(hash 首先是一个数组~~,数组上有些位置有元素,有些没有元素)
ziplist 付出的代价,进行读写元素,速度是比较慢的,如果元素个数少,慢的并不明显, 如果元素个数太多了,慢就会雪上加霜,
如果,
1.哈希中的元素个数比较少,使用 ziplist 表示.元素个数比较多,使用 hashtable 来表示2.每个 value 的值长度都比较短,使用 ziplist 表示.如果某个 value 的长度太长了,也会转换成 hashtable
- hash-max-ziplist-entries 配置(默认 512 个)
- hash-max-ziplist-value 配置(默认 64 字节)
- 这个配置项就是可以写到 redis.conf 文件中的~~
5.hash的应用
5.1 作为缓存
string 也是可以作为缓存使用的.
存储结构化的数据(类似于 数据库 表 这样的结构~~),使用 hash 类型更合适一些~~
上述场景使用 string 类型也能做到,
就需要使用到 json 这样的数据格式
- 如果使用 string(ison)的格式来表示 Userlnfo万一只想获取其中的某个 field, 或者修改某个 field ~~就需要把整个 json 都读出来, 解析成 对象,操作 field,再重写转成 json 字符串,再写回去~~
- 如果使用 hash 的方式来表示 Userlnfo,就可以使用 field 表示对象的每个属性(数据表的每个列)此时就可以非常方便的修改/获取任何一个属性的值了~~
- 使用 hash 的方式,确实读写 field 更直观高效,但是付出的是空间的代价~~需要控制哈希在 ziplist 和hashtable 两种内部编码的转换,可能会造成内存的较大消耗。
高内聚
把有关联的东西放在一起,最好能放在指定的地方~~
耦合
- 两个模块/代码 之间的关联关系,关联关系越大,越容易相互影响认为是耦合越大~~
- 追求的是"低耦合,避免"牵一发动全身"这边一改出 bug,影响到了其他的地方
哈希类型和关系型数据库有两点不同之处:
• 哈希类型是稀疏的,而关系型数据库是完全结构化的,例如哈希类型每个键可以有不同的 field,而关系型数据库⼀旦添加新的列,所有⾏都要为其设置值,即使为 null【稀疏更加节省空间】• 关系数据库可以做复杂的关系查询,⽽ Redis 去模拟关系型复杂查询,例如联表查询、聚合查询等基本不可能,维护成本⾼。