Redis中数据范例的利用(hash和list)
(一)hash哈希我们知道redis中的数据都是以键值对的方式存储的,key全部都是string范例,而value可以是不同的数据结构,其中就包括hash,也就是说,key这一层组织完成后到了value仍旧是hash
1.Hash的一些下令
1)hset
设置hash中指定的字段(filed)和值value
https://i-blog.csdnimg.cn/direct/9c3295861c9042a8a43b4740c58ab642.png
我们首先要确定一个key,然后在这个key的value中,再添加一个hash结构
https://i-blog.csdnimg.cn/direct/7f023c18cb554e3aa157391abc2201b4.png
同时我们可以利用这个下令,同时添加多个key对应的哈希结构value
https://i-blog.csdnimg.cn/direct/391162d153db49cbb3834f4b93ce924d.png
时间复杂度是O(1)如果插入多组就是O(n)
2)hget
获取hash中指定字段的值
https://i-blog.csdnimg.cn/direct/c98bca0b6a6248a5b2b6929e413fe0ba.png
我们必要给定一个key,然后再给定一个key中保存的filed
https://i-blog.csdnimg.cn/direct/723e5c08a95744f889ff9bb46b8a8be6.png
时间复杂度是O(1),如果我们查询的字段不存在就会返回nil
https://i-blog.csdnimg.cn/direct/c9583d7e7b8c4d6bae39f1a1ad5e7d51.png
3)hexists
判断hash中是否有对应的field
https://i-blog.csdnimg.cn/direct/650cba68eacb4c86911487fe8cfbac1a.png
返回值如果是1就表示存在,如果是0就表示不存在
时间复杂度也是O(1)
4) hdel
删除hash中指定的field,我们要注意,del是删除的key,而hdel是删除的field
https://i-blog.csdnimg.cn/direct/2e972aff574b418d8393b0aa2a4b8de3.png
我们可以通过这个下令,一次性删除多个field
时间复杂度:删除一个元素为O(1),n个元素为O(n)
5)hkeys
获取hash中的全部field
https://i-blog.csdnimg.cn/direct/8d04fb050f734c8f971daeaa39ea75b3.png
时间复杂度为O(n) ,N为field的个数
6)hvals
获取hash中的全部value
https://i-blog.csdnimg.cn/direct/b4fb2477b7c04382bf4baf7b311f03a8.png
获取hash中的全部值
时间复杂度为O(n),N为field的个数
7)hgetall
获取hash中的field和value
https://i-blog.csdnimg.cn/direct/19e912f9279c460ab6ff505e8860fa70.png
时间复杂度O(n),N为field的个数
8)hmget
一次获取多个field中的value,我们刚刚的hget一次只能获取一个,而且之前说过,为了保证效率,我们要只管减少网络的开销,以是我们可以利用hmget来一次获取多个value
https://i-blog.csdnimg.cn/direct/cbc1f889bf694acfa439621302c646ff.png
这里实在另有hmset,可以一次性放入多个field和value,但是hset本身支持这个功能,以是这里我们不做阐明
时间复杂度O(n),N为field的个数
9)hlen
获取hash中全部field的个数
https://i-blog.csdnimg.cn/direct/9f1a06a9a53a4bd695b01025ddabac4e.png
时间复杂度O(1)
返回值为field的个数
10)hsetnx
在field不存在情况下设置hash中的field和value,如果存在就会失败,与setnx雷同
https://i-blog.csdnimg.cn/direct/0562148e77af48e192c863763b055f63.png
时间复杂度为O(1),0表示失败,1表示成功
11)hincrby
用来使field对应的value举行+n的操作,我们都知道hash用来存键值对结构,以是固然也可以用来计数
https://i-blog.csdnimg.cn/direct/accdf032808747758780271acb2cdce3.png
但是我们利用这个下令时要注意,我们改变的值和增加的值,都必要是整数,不然就会报错
https://i-blog.csdnimg.cn/direct/505bf8af1dfe4cca9c79e3e35ef272bb.png
时间复杂度为O(1),返回值为变化后的值
12)hincrbyfloat
就是上一条指令的浮点数版本,用法是一样的,这里不多赘述
下令小结
https://i-blog.csdnimg.cn/direct/ec5938d2f005445a9669eb2291048397.png
https://i-blog.csdnimg.cn/direct/5577b9a1f1e646d5b84ef7938d113088.png
我们上述也说了一些,一次查询多条数据的指令,但是我们在利用的时候要注意,我们redis可能做缓存也可能做服务器,以是如果我们查询的数据许多,就会导致阻塞,可能会导致出现服务器一瞬间压力过大引发一系列其他问题,而且我们上述的h系列的下令,必须要保证key对应的value必须是hash范例的
那如果我们肯定要获取到全部的field数据,我们可以利用hscan,这个遍历redis的hash的渐进式遍历的,也就是一次遍历一小部门,分多次遍历全部的field,这样就不会阻塞住redis了
2.hash的内部编码
hash的内部编码有两种
ziplist(压缩列表):当hash范例的元素个数小于肯定值时,redis就会利用ziplist来作为hash的内部实现,利用ziplist可以更加进奏的实现多个元素的连续存储,以是可以很好的节省空间,但是如果我们hash范例元素过多,就会导致读写效率会变得很慢
hashtable(哈希表):当元素个数比力多时,redis就会利用hashtable来作为内部实现,由于我们说元素个数多,就会导致ziplist的读写效率下降,但是hashtable的读写时间复杂度为O(1)
也就是说,ziplist是用时间换空间,但是hashtable是用空间换时间,而我们之前也说过,空间的话我们如今的硬件还是比力够用的,但是时间还是必要我们尽可能的去节省
https://i-blog.csdnimg.cn/direct/fcc546f311f14cfa82bcec2452a539fa.png
https://i-blog.csdnimg.cn/direct/a50db042d97e4f3a8219a25642707ebc.png
3.hash的一些应用场景
1)作为缓存
我们之前利用的mysql是关系型数据库,用户的属性和信息表现为一个表
但是我们redis是利用一个个键值对,以是就会通过映射的方式来表示用户的信息,我们可以利用字符串的json格式
https://i-blog.csdnimg.cn/direct/6e7710f41f69401bbfa2d2cb12bda4a2.png
但是这样仍旧不够直观,我们就可以通过本日的hash范例来存储
缓存方式对比:
1).如果我们利用原生的string范例
https://i-blog.csdnimg.cn/direct/f97c1923686646dd9e738baa88a0f3b6.png虽然也可以表示用户信息实现也很简朴,但是会导致内存的占用量比力大,而且用户信息比力分散,由于每个key都是不同的,不满足高内聚的特点
2).如果我们序列化字符串利用json格式
优点:针对总是以团体作为操作的信息⽐较符合,编程也简朴。同时,如果序列化⽅案选择符合,内存的使⽤效率很⾼
缺点:我们序列化和反序列化也有肯定的开销,如果总操作个别属性就不是很灵活
3).hash范例
优点:简朴,灵活,直观,同时可以很方便的存储和获取信息
缺点:在内部涉及到内部编码ziplist和hashtable的转换,可能会对内存造成消耗
(二)list列表
list就相称于顺序表,支持头插头删,尾插尾删,以是list内部的编码方式并非是一个简朴的数据,更像是双端队列
https://i-blog.csdnimg.cn/direct/0ba3c769220148d2a78ab79eb2e3477f.png
同时,列表的元素是有序的,这里的有序是指顺序不同会导致结果不同,并不是升序和降序的有序,而且列表中的元素的允许重复的
1.list的一些下令
1)lpush
用来头插到list中(左头右尾),支持同时插入多个元素
https://i-blog.csdnimg.cn/direct/c18d555eaffe4237b004bb689e0653cf.png
时间复杂度O(1),如果插入多个元素就为O(n)n为插入元素的个数
返回值是插入后list的长度
2)lpushx
当key存在时就将元素头插到list中,不存在就直接返回
https://i-blog.csdnimg.cn/direct/0b628bb792ff41398d4cb265928cbf4b.png
时间复杂度:只插⼊⼀个元素为O(1),插⼊多个元素为O(N),N为插⼊元素个数.
返回值:插⼊后list的⻓度。
3)rpush
把一个或多个元素尾插到list中
https://i-blog.csdnimg.cn/direct/ecda3c3934d3438d9f5c3f480db6730e.png
时间复杂度为O(1),插入N个为O(N)
返回值为list插入后的长度
4)rpushx
当key存在时就把一个或多个元素尾插到list中
https://i-blog.csdnimg.cn/direct/50f57621fddc43a0a29ddf24d525c2dc.png
时间复杂度:只插⼊⼀个元素为O(1),插⼊多个元素为O(N),N为插⼊元素个数.
返回值:插⼊后list的⻓度
5)lrange
我们这里的l是代表list并不是left
获取从start开始到end区间的全部元素,左右都是闭区间,并且由于redis支持负数下标,如果我们要获取到末了一个元素可以利用-1,并且redis下标与数组一样,都是从0开始
https://i-blog.csdnimg.cn/direct/2da300e3c4ab4b9bb8de109cb6701a7d.png
同时这里redis有一个很好的点
我们在c++中,如果下标超出了范围,我们一样平常会认为这是一个“未定义的行为”可能会导致步调瓦解,也可能会出现不正当或者正当的数据,这就会导致我们不肯定会立刻发现问题,但是这种不负责的行为,效率确实是比力高
在java中,如果我们下标超出范围,会给我们抛出异常,但是由于要给我们多做一步下标正当性的验证,就会导致速率会比力慢,但是我们可以第一时间发现问题
而redis是尽可能的去获取到给定区间的元素,如果我们给了一个非法区间,比如超出下标,那么redis也会返回可以获取到下标元素的值(拥有鲁棒性,容错能力强)
https://i-blog.csdnimg.cn/direct/187e20f70d6048b2886ccdacaa85eb31.png
6)lpop
头删,从list左侧取出元素
https://i-blog.csdnimg.cn/direct/edacec4ff51f4ac0b3c9985b5ff6210b.png
时间复杂度:O(1)
返回值:取出的元素或者nil。
7)rpop
尾删,从list右侧取出元素
https://i-blog.csdnimg.cn/direct/d70535f01612478e8042ed0d14fb127b.png
时间复杂度:O(1)
返回值:取出的元素或者nil。
我们这里要注意,在当前的redis5中并没有count参数,但是redis6.2以后,新增了count参数,描述这一次要删几个元素
同时搭配这几个出队入队操作,可以实现,栈,队列,双端队列等数据结构
8)lindex
获取从左数第index位置的元素
https://i-blog.csdnimg.cn/direct/f3d342eec45d4488a359037be532d533.png
时间复杂度:O(N)
返回值:取出的元素或者nil。
9)linsert
在特定的位置插入元素
https://i-blog.csdnimg.cn/direct/39763fd6a02e4c91b5aaa69b5ccc9b39.png
我们发现我们在111签名插入1010,但是有两个111,以是我们会在最前面的一个111前面加
时间复杂度为O(N),返回值为插入后的list长度
10)llen
获取到list的长度
https://i-blog.csdnimg.cn/direct/112e8cf5b96c4c5cb95c289d7b7f556a.png
时间复杂度O(1),返回值为list的长度
11)lrem
删除某个值的一些元素
https://i-blog.csdnimg.cn/direct/c9d5c0983ffa41578f2b9b23f5c8a015.png
https://i-blog.csdnimg.cn/direct/376da6b6c5df43639458c21787bc14be.png
12)ltrim
保留start和stop之间的元素,两边外的元素直接被删除
https://i-blog.csdnimg.cn/direct/20d1c69b0f8c4c168388d2b7847ca646.png
13)lset
根据下标修改元素
https://i-blog.csdnimg.cn/direct/d5741e02372c42ae824c7cfe231dedba.png
我们如果想在不存在的下标上设置元素会直接返回nil
如果我们想删除一个指定位置的元素,我们可以通过lset把这个位置的元素设置为一特定的字符,然后通过lrem来举行删除
2.阻塞版本的下令
blpop和brpop是lpop和rpop的阻塞版本,他们的区别为:
1).如果列表有元素,那么阻塞和非阻塞是一样的,如果没有元素,非阻塞版本会返回nil,阻塞版本会根据阻塞时间,举行阻塞,这是redis可以执行一些别的下令,但是要求执行下令的客户端为阻塞状态。
2).下令中如果设置了多个间,就会从左向右遍历,一旦有一个键对应的列表有元素,就会弹出并且返回
3).如果有多个客户端执行blpop,最先执行下令的客户端会得到弹出的元素
我们redis中的list就相称于一个阻塞队列,我们之前在多线程上也说到过阻塞队列,但是当时我们必要手动保证线程安全,但是redis的线程安全是通过线程安全的情况,而由于我们redis的空间还是比力大的,一样平常不考虑队列满的情况
blpop
lpop的阻塞版本
https://i-blog.csdnimg.cn/direct/c3c5527e12094df381fbae8053b3a94e.png
返回值为列表和取出的元素或者nil
时间复杂度O(1)
brpop
rpop的阻塞版本
https://i-blog.csdnimg.cn/direct/33de858a1cfd4e159c9c2f42871d5771.png
时间复杂度O(1)
返回值为取出的元素或nil
3.lsit的内部编码
1.ziplist(压缩列表)
我们之前在hash也说过,他的内部编码也是ziplist,ziplist把数据按照更紧凑的压缩情势举行标识,能够很好的节省空间,但是如果数据比力多操作数据的效率就会比力低
2.linkedlist(链表)
ziplist如果数据多,操作数据的效率会比力低,以是我们可以通过linkedlist来操作数据,虽然这样会使我们占用的空间变大,但是对数据的操作会比力快
但是这两种方式各有各的好处,有没有一种编码方式可以综合这两种的特点?
我们redis在之后利用了quicklist来内部编码list范例,quicklist相称于使list和ziplist的结合,团体还是一个链表,但是链表的每一个节点,是一个ziplist,我们通过链表来保证每一个ziplist都不是很大,这样就可以在节省空间的前提下,尽可能的快速的操作数据
4.利用场景
消息队列
我们说list给我们提供了阻塞状态的lpop和rpop,以是我们可以用这个阻塞实现生产者消费者模子,我们生产者客户端用lpush从列表左边插入元素,然后消费者用brpop来阻塞式的去拿队首元素
https://i-blog.csdnimg.cn/direct/47a6196d4bbd4fce8e385f92d59ab7b6.png
分频道的消息队列
redis利用lpush和brpop下令,但是通过不同键模仿不同频道的概念,不同的消费者可以通过brpop不同的键
作为数组
我们可以把list作为雷同数组的一个结构,来存储多个元素,具体要如何组织数据,必要根据实际业务来确定
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页:
[1]