4 哈希表
哈希类型中的映射关系通常称为field-value,⽤于区分Redis整体的键值对(key-value), 注意这⾥的value是指field对应的值,不是键(key)对应的值,
4.1 操作命令
hset:设置hash中指定的字段(field)的值(value)。
HSET key field value [field value ...]
//一个命令可以设置多个filed,value
返回值:添加成功的f-v的个数。
hget:获取hash中指定字段的值。
HGET key field
时间复杂度:O(1)
返回值:字段对应的值或者nil。
hexists:判断hash中是否有指定的字段。
HEXISTS key field
返回值:1表⽰存在,0表⽰不存在。
HDEL :删除hash中指定的字段。
HDEL key field [field ...],hdel删除的是key中的filed
时间复杂度:删除⼀个元素为O(1).删除N个元素为O(N).
返回值:本次操作删除的字段个数。没有的话返回0.
del操作就会直接将key整个删除。
hkeys:获取hash中的所有字段。该操作西安根据key找到对应的hash,然后再遍历hash
HKEYS key
时间复杂度:O(N),N为field的个数.
返回值:字段列表。
HVALS :获取hash中的所有的值。
HVALS key
时间复杂度:O(N),N为field的个数.
返回值:所有的值。
HGETALL :获取hash中的所有字段以及对应的值。
HGETALL key
时间复杂度:O(N),N为field的个数.
返回值:字段和对应的值。
界面显示是一个k一个value,一一对应。
HMGET ⼀次获取hash中多个字段的值。
HMGET key field [field ...]
时间复杂度:只查询⼀个元素为O(1),查询多个元素为O(N),N为查询元素个数.
返回值:字段对应的值或者nil。
上述的hkeys,hvals,hgetal都是存在一定的风险的,hash的元素个数太多,执行命令就会耗很长的时间,从而阻塞redis。hscan也可以遍历redis的hash,但是它属于“渐进式遍历”l,即执行一次命令,就遍历一部分。
concorrenthashmap:是java标准库提供的线程安全的哈希表,在哈希表扩容的时候就是按照化整为零的方式进行的。
HLEN 获取hash中的所有元素的个数。
HLEN key
时间复杂度:O(1)
返回值:字段个数。
HSETNX 在字段不存在的情况下,设置hash中的字段和值。
HSETNX key field value
时间复杂度:O(1)
返回值:1表⽰设置成功,0表⽰失败。
HINCRBY 将hash中字段对应的数值添加指定的值。
HINCRBY key field increment
时间复杂度:O(1)
返回值:该字段变化之后的值
4.2 redis-hash的内部编码
哈希的内部编码有两种:
• ziplist(压缩列表):当哈希类型元素个数⼩于hash-max-ziplist-entries配置(默认512个)、 同时所有值都⼩于hash-max-ziplist-value配置(默认64字节)时,Redis会使⽤ziplist作为哈 希的内部实现,ziplist使⽤更加紧凑的结构实现多个元素的连续存储,所以在节省内存⽅⾯⽐ hashtable更加优秀。
所谓压缩,rar,zip,gzip,7z实际上是一些具体的压缩算法,压缩的本质是针对数据进行重新编码,不同的数据有不同的特点,结合这些特点进行设计,重新编码之后能够缩小体积。
ziplist内部的数据结构也是精心设计的,一个普通的hash表由于有些位置上有元素有些位置上没有元素会浪费一定的空间,ziplist目的是节省内存空间,但是读写元素的速度比较慢。
1、如果hash中的元素比较少使用ziplist表示,如果元素的个数比较多就使用hashtable来表示;
2、如果每一个value的值的长度比较短就使用ziplist表示,如果长度比价长就会转换为hashtable来表示。
• hashtable(哈希表):当哈希类型⽆法满⾜ziplist的条件时,Redis会使⽤hashtable作为哈希 的内部实现,因为此时ziplist的读写效率会下降,⽽hashtable的读写时间复杂度为O(1)。
4.3 hash的应用
作为缓存,存储结构话的数据,使用hash更好。
上述结构性数据如果使用string类型的字符串,就需要使用json格式,如果使用string(json)来表示userinfo,如果只是想从内存中获取某个field,就需要吧整个json都读出来,解析成一个对象操作field,完成之后在转换成json字符串写回到内存中。
如果使用hash的方式来表示userinfo,就可以使用field表示对象的每一个属性(数据库的每一个列),此时就可以方便的修改或获取每一个属性的值。
故此使用hash的方式,读写field更加高效,但是付出的是空间的代价,需要控制哈希ziplist和hashtable两种内在的编码的转换,需要造成内存的极大消耗。
高内聚:将大部分有关联得东西放到一起。
耦合:多个模块/代码之间的关联关系,一般追求的是低耦合。
需要注意的是哈希类型和关系型数据库有两点不同之处:
• 哈希类型是稀疏的,⽽关系型数据库是完全结构化的,例如哈希类型每个键可以有不同的field,⽽ 关系型数据库⼀旦添加新的列,所有⾏都要为其设置值,即使为null。
• 关系数据库可以做复杂的关系查询,⽽Redis去模拟关系型复杂查询,例如联表查询、聚合查询等 基本不可能,维护成本⾼。
5.列表list
list相当于数组或者顺序表。
list内部的编码方式并非是一个简单的数组,而是类似于“双端队列”(deque)。
列表类型的特点:
第⼀、列表中的元素是有序的(根据上下文区分),(有时候的有序是指升序和降序,这里的有序是指顺序),如果吧元素的位置进行颠倒,此时得到的list和之前的list的不等价的。
第⼆、区分获取和删除的区别。lindex能获取元素的值,lrem也能返回被删除元素的值。
第三、列表中的元素是允许重复的。像hash类型,其field是不能重复的。因为当前的list头和尾都能高效的插入删除元素,就可以把这个list当做是一个栈/队列来使用。
5.1 操作命令
LPUSH :将⼀个或者多个元素从左侧放⼊(头插)到list中,可以一次插入一个或者多个。如果key已经存在,且key对应的value类型不是list,此时lpush命令就要报错
LPUSH key element [element ...]
时间复杂度:只插⼊⼀个元素为O(1),插⼊多个元素为O(N),N为插⼊元素个数.
返回值:插⼊后list的⻓度。
lrange:获取从start到end区间的所有元素,左闭右闭。
LRANGE key start stop
时间复杂度:O(N)
返回值:指定区间的元素。
redis对于给定的不合法的区间获取元素的操作,会尽可能的在不合法的区间里面尽量多的获取元素。
LPUSHX :在key存在时,将⼀个或者多个元素从左侧放⼊(头插)到list中。不存在,直接返回
LPUSHX key element [element ...]
时间复杂度:只插⼊⼀个元素为O(1),插⼊多个元素为O(N),N为插⼊元素个数.
返回值:插⼊后list的⻓度。
RPUSHX :在key存在时,将⼀个或者多个元素从右侧放⼊(头插)到list中。不存在,直接返回
RPUSHX key element [element ...]
时间复杂度:只插⼊⼀个元素为O(1),插⼊多个元素为O(N),N为插⼊元素个数.
返回值:插⼊后list的⻓度。
RPUSHX :在key存在时,将⼀个或者多个元素从右侧放⼊(尾插)到list中。
RPUSHX key element [element ...]
时间复杂度:只插⼊⼀个元素为O(1),插⼊多个元素为O(N),N为插⼊元素个数.
返回值:插⼊后list的⻓度。
lpop:从list 左侧取出元素(即头删)。
LPOP key
RPOP :从list 右侧取出元素(即尾删)。
RPOP key
类似于队列:使用rpush和lpop;
类似于栈,使用rpush和rpop。
LINDEX 获取从左数第index位置的元素。
LINDEX key index
lrem:从左边删除
lrem key count element
count要删除的个数,element要删除的值;
count>0:从左往右开始,删除遇到的count个element元素。
count<0:从右往左开始,删除遇到的count个element元素。
count=0,删除所有的element元素。
ltrim:指定范围,范围内的保留,其余的删除
ltrim key start stop左闭右闭
lset:根据下标修改元素
lset key index e
o(n)
5.2 阻塞版本命令
阻塞:当前的线程不走了,代码不继续执行了,会在满足一定的条件后被唤醒。blpop和brpop。
redis的list相当于阻塞队列,线程安全室通过单线程模型支持的。只支持队列为空的状态,不支持队列满的状态。即:
但是阻塞版本会根据timeout,阻塞一段时间,器件redis可以执行其他命令。使用brpop和blpop的时候,是可以显式设置阻塞时间的。
1、blpop和brpop都是可以同时去尝试获取多个key的列表的元素的。
2、如果多个客户端同时对一个key执行pop,则最先执行命令的客户端会得到弹出的元素。
blpop key [key..] timeout
这里的key可以是指定一个或者多个,每一个key对应一个list。
如果这些list有任何一个非空,blpop都能够把这里的元素给获取到,立即返回。
如果这些list都为空,此时就需要阻塞等待,等待其他的客户端往这些list中插入元素了,同时还可以设置超时时间。
返回结果首先是该key,其次是获取到的元素。
5.3 命令小结
内部编码:
redis5之前:
列表类型的内部编码有两种:
ziplist(压缩列表):当列表的元素个数⼩于list-max-ziplist-entries配置(默认512个),同时 列表中每个元素的⻓度都⼩于list-max-ziplist-value配置(默认64字节)时,Redis会选⽤ ziplist来作为列表的内部编码实现来减少内存消耗。
linkedlist(链表):当列表类型⽆法满⾜ziplist的条件时,Redis会使⽤linkedlist作为列表的内部实现
当前以redis5v为例:
ziplist(压缩列表):把数据按照更紧凑的压缩形式进行表示的,方便节省空间,但是当元素个数多了,操作起来效率会降低。
linkedlist(链表):很好的应对元素个数较多的情况。
quicklist:相当于链表和压缩列表的结合,整体是一个链表,但是链表的每一个节点都是一个压缩列表。每一个压缩列表都不让其过大,将多个压缩列表通过链式结构连接起来。
对于ziplist最大空间的配置于:list-max-ziplist-size -2;表示最大的空间大小为8kb。
5.4 应用场景
1、用list作为数组这样的结构,来存储多个元素。
2、作为消息队列。
ps:谢谢观看,壁纸来源于【PV】下次旅行(又名大理风景片)-刘力菲_哔哩哔哩_bilibili