Redis数据结构-Hash哈希
1.Hash哈希
几乎所有的主流编程语言都提供了哈希(Hash)类型,它们的叫法可能是哈希,字典,关联数组,映射。在Redis中,哈希类型是指值本身又是一个键值对结构,形如key="key",value={{field1,value1},{field2,value2},...,{fieldN,valueN}};
Redis键值对和哈希类型两者的关系可以用下图表示
哈希中映射关系通常称为field-value,用于区分Redis整体的键值对(key-value)
2.常见命令
2.1 HSET
设置Hash中指定的字段(field)的值(value)
语法:HSET key field value [field value ...]
时间复杂度:插入一组field为O(1),插入N组field为O(N)
返回值:添加的字段的个数
示例:
2.2 HGET
获取hash中指定的字段的值
语法:HGET key field
时间复杂度:O(1)
返回值:字段定影的值或nil
示例:
2.3 HEXISTS
判断hash中是否有指定的字段
语法:HEXISTS key field
时间复杂度:O(1)
返回值:1表示存在,0表示不存在
示例:
2.4 HDEL
删除hash中指定的字段
语法:HDEL key field [field ...]
时间复杂度:删除一个元素为O(1),删除N个元素为O(N)
返回值:本次操作删除的字段个数
示例:
2.5 HKEYS
获取hash中的所有字段
语法:HKEYS key
时间复杂度:O(N),N为field的个数
返回值:字段列表
示例:
方法存在风险,类似与keys *,找到对应的hash O(1),但是需要遍历hash O(n)
2.6 HVALS
获取hash中所有的值
语法:HVALS key
时间复杂度:O(N),N为field的个数
返回值:所有值
示例:
2.7 HGETALL
获取hash中所有字段以及对应的值
语法:HGETALL key
时间复杂度:O(N),N为field的个数
返回值:字段和对应的值
示例:
上述hkeys,hvals,hgetall都是一条指令都完成所有遍历,都存在一定风险的,hash元素个数太多,执行的时间比较长,从而阻塞redis; hscan遍历redis的hash,但是hscan属于渐进式遍历,一次命令,遍历一小部分,再敲一次,再遍历一小部分(时间可控),连续多次,完成整个遍历,这个过程和Java中的ConcurrentHashMap扩容时相似,一批一批搬运元素。
2.7 HMGET
一次获取hash中多个字段的值
语法:HMGET key field [field ...]
时间复杂度:只查询一个元素为O(1),查询多个元素为O(N),N为查询元素个数
返回值:字段对应的值或者nil
示例:
2.8 HLEN
获取hash中所有字段的个数
语法:HLEN key
时间复杂度:O(1)
返回值:字段个数
示例:
2.9 HSETNX
在字段不存在的情况下,设置hash中的字段和值
语法:HSETNX key field value
时间复杂度:O(1)
返回值:1表示设置成功,0表示失败
示例:
2.10 HINCRBY
将hash中字段对应的数值添加指定的值
语法:HINCRBY key field increment
时间复杂度:O(1)
返回值:该字段变化之后的值
示例:
2.11 HINCRBYFLOAT
HINCRBY的浮点数版本
语法:HINCRBYFLOAT key field increment
时间复杂度:O(1)
返回值:该字段变化之后的值
示例:
2.12 HSTRLEN
获取field对应value的长度
语法:HSTRLEN key field
时间复杂度:O(1)
返回值:返回字段对应值的长度,如果字段不存在返回0
示例:
3.Hash的内部编码
哈希的内部编码有两种:
3.1 ziplist
ziplist(压缩列表):当哈希类型元素个数小于hash-max-ziplist-entries配置(默认为512个),同时所有值小于hash-max-ziplist-value配置(默认为64个字节)时,Redis会使用ziplist作为哈希的内部实现,ziplist使用更紧凑的结构实现多个元素的连续存储,所以在节省内存方面比hashtable更加优秀,但是读写元素比较慢
3.2 hashtable
hashtable(哈希表):当哈希类型无法满足ziplist的条件时,Redis会使用hashtable作为哈希的内部实现,因为此时ziplist的读写效率会下降,而hashtable的读写时间复杂度为O(1)
4.Hash的典型场景
4.1 映射关系表示用户信息
相比于使用JSON格式的字符串存储用户信息,哈希类型变得更加直观,并且在更新操作上变得更灵活。可以将每个用户的id定义为键后缀,多对field-value对应用户的各个属性。
哈希类型和关系型数据库的不同:
1.哈希表是稀疏的,而关系型数据库时完全结构化的,列入哈希类型每个键可以有不同的field,而关系类型数据库一旦添加新的列,所有行都要为其设置值,即使为null
2.关系类型数据库可以使用复杂的关系查询,而Redis去模拟关系类型复杂查询,例如联表查询,聚合查询等基本不可能,维护成本高
5.缓存方式对比
1.原生字符串---使用字符串类型,每个属性一个键
set user:1:name hajimi
set user:1:age 21
set user:1:city Luoyang
优点:实现简单,针对个别属性变更也会很灵活
缺点:占用过多的键,内存占用量比较大,同时用户信息在Redis这种比较分散,缺少内聚性,所以这会方案基本没有实用性
2.序列化字符串类型,如JSON格式
set user:1 经过序列化的用户对象字符串
优点:针对总是以整体为操作的信息比较合适,编程也简单。同时如果序列化方案选择合适,内存的使用效率很高
缺点:本身序列化和反序列化需要一定开销,同时如果总是操作个别属性则非常不灵活
3.哈希类型
hmset user:1 name hajimi age 21 city Luoyang
优点:简单,直观,灵活。尤其是针对信息的局部变量变更或者获取操作
缺点:需要控制哈希在ziplist和hashtable两种内部编码方式的转换,可能会造成内存的较大消耗