Redis远程字典服务器(6) —— list类型详解

目录

一,基本情况

二,list常用命令

2.1 lpush,lrange

2.2 对于“下标越界”的思考 

2.3 lpushx,rpush,rpushx

2.4 lpop,rpop

2.5 lindex,linsert,llen 

2.6 lrem

2.7 ltrim,lset

2.8 阻塞版本命令

 2.9 blpop,brpop

三,内部编码

四,应用场景

4.1 作为数组

4.2 作为消息队列


一,基本情况

  1. 列表(list)相当于数组或者顺序表,约定最左边元素下标为0,后面依次递增;但是Redis的list支持负数下标,规定最右边的下标为-1,往左依次递减,如下图:
  2. 如上图,list支持左右两边的插入和删除:lpus,lpop,rpush,rpop。
  3. 所以list内部的编码方式,并非一个简单的数组,而是接近于deque那样的双端队列

 list类型特点

  • 列表中的元素是有序的:“有序”的含义要根据上下文区分,此处的“有序”不仅仅是升序降序,指的是顺序很关键(如果把元素位置调换,得到的新的list和旧的list是不等价的;当调换后新list和旧list一样,就称之为顺序不关键),所以一个词要怎么理解,务必要结合上下文,结合具体场景去理解
  • 区分获取元素和删除元素的区别:lrem是删除,lindex是获取,两个命令的返回值是一样的,容易混淆。
  • list中的元素允许重复:像hash这样的类型,field不能重复
  • 因为当前的list,能头和尾都能插入和删除,可以把list当作一个栈或者队列来使用

二,list常用命令

命令查询文档传送门:Commands | Docs (redis.io)

2.1 lpush,lrange

lpush表示从列表左边头插元素(插入1,2,3,4,完成操作后4在最前面),可以一次插入多个,能减少网络开销;lrange作用是查询指定区间的值,有三个参数,第一个为key,第二个和第三个为指定区间的开始下标和结束下标,是一个闭区间:

要想一次查询所有元素,只需要将lrange的开始设为0,结尾设为-1即可: 

注意

  • 如果key已经存在并且对应的value不是list,那么lpush就会报错 
  • lrange前面显示的序号和下标无关,是专门给结果集使用的序号,给我们看的。(hash类型操作也有这样的序号,但是也和下标无关,因为hash没有下标的概念)

2.2 对于“下标越界”的思考 

问题:谈到下标,往往会有“超过范围”这样的情况,如何理解?

解答: C++中,下标超出范围,一般会认为这是一个“未定义行为”,可能会导致程序崩溃,也可以会得到一个不合法的数据,还有可能会得到一个“看起来合法,但是错误”的数据,还有可能得到符合要求的数据(类似开盲盒);而在Java中,下标超出范围,一般会“抛异常”。

而在Redis中,两种行为都为采用,如果区间是合法的就正常搞,如果不合法,就直接尽可能去获取对应的内容,这种处理方式接近于Python的“切片”的处理方式。

比如,盆友找我借100,但是我只有50,盆友说:“50就50吧,借我一下谢谢”,能给多少给多少这样。

C++和Java对于越界的处理方式,有哪些优点缺点呢?

C++的处理方式:

  • 优点:效率是最高的,因为Java要抛异常,就代表Java要多出一步“下标合法性验证”的步骤,做的工作多了,效率就低了
  • 缺点:程序员不一定能第一时间发现问题,而且很难发现,就导致最后的结果是“带伤运行”,最后越积越多,导致严重后果

Java的处理方式:

  • 优点:出现问题能及时发现
  • 缺点:效率没C++高,因为多了一步

Redis或Python的处理方式,是一种更加柔和式的做法,称之为“鲁棒性”(你对我越粗鲁,我就表现得越棒),这样的设定能大大提高程序的“容错性”,但是也有代价,所以这三种方式的实现,还是需要集合具体场景去搞

2.3 lpushx,rpush,rpushx

lpushx作用是,如果key存在,就头插操作,如果不存在就什么都不做;rpush就是尾插,其余机制和lpush一样;rpushx和lpushx的作用一样,只是变成了尾插:

问题:有lrange,有没有rrange呢?

解答:没有,因为lrange,延长来说是list range,不是left range 

2.4 lpop,rpop

lpop头删,,当key不存在,返回nil;rpop尾删,当key不存在,返回nil

注意

在从Redis 6.2 版本中,新增了count参数,但是当前的Redis 5 版本没有,count表示一次要删除几个数,因为一次只删一次相比一次删多个效率确实低了点  

2.5 lindex,linsert,llen 

lindex作用是根据下标来获取元素,下标非法返回nil;linsert就是在指定下标插入元素,可以自定义在左边插入还是在右边插入,下标非法返回nil;llen返回list长度,key不存在返回0:

lindex: 

linsert: 

linsert key BEFORE|AFTER pivot element

先从左往右根据基准值找到符合要求的位置,再进行插入 

llen

2.6 lrem

lrem key count element

count表示要删除的个数,element表示要删除的值

关于count还有一些说法,官方文档给出的解释如下:

解释一下: 

  • 当count > 0:从左往右去找,比如list值为“1 2 3 4 1 2 3 4 1 2 3 4”,count为2,element为1,表示从左往右找1,删两次1,结果变成“2 3 4 2 3 4 1 2 3 4”,前面两个1被删除
  • 当count < 0:从右往做找,以上面的为例,count为-2,element为1,表示从右往左找两个1删除,最后变成“1 2 3 4 2 3 4 2 3 4”,后面的两个1被删除
  • 当count = 0:删除所有的指定元素,还是以上面的为例,count为0,element为1,就找到所有的1删除,结果为“2 3 4 2 3 4 2 3 4

2.7 ltrim,lset

ltrim作用也是删除元素,但是是反过来的,指定一个区间,保存这个区间里的元素,区间外的就删除,区间不合法时返回0;lset作用是根据下标修改元素:

ltrim: 

在官方文档中查询ltrim时,还有一个东西: 

  1. 其中ACL全程是access control list,访问控制列表,是一个和权限相关的东西,从Redis 6 版本开始支持
  2. Redis有很多命令,acl这块就把每个命令打上一些标签,比如上面的@write表示这是一个“写”命令,@list表示这是一个和list类型相关的命令,@slow表示这个命令可能会很耗时
  3. 打好标签之后,管理员给每个Redis用户配置不同的权限,让该用户执行“能执行”的命令

lset: 

lset key index element

index表示要开始修改的下标位置,element表示要修改的元素

2.8 阻塞版本命令

阻塞:当前的线程不走了,代码不继续执行了,在满足一定条件后被唤醒

blpop,brpop是阻塞命令中两个最重要的,前面的b表示block阻塞

我们在学习生产者消费者模型时,讲到了一个阻塞队列Linux系统编程——生产者消费者模型_编程 消费者-CSDN博客

 用队列来作为交易场所,并且希望这个队列有两个特性:1,线程安全    2,阻塞:

  • 如果队列为空,此时尝试出队列,就阻塞,直到队列不为空,阻塞接触
  • 如果队列为满,此时尝试入队列,就阻塞,直到队列不为满,阻塞接触

Redis中list也相当于一个阻塞队列, 首先线程安全通过单线程模型能保证,而阻塞只支持“队列为空”的情况,不考虑“队列为满”。

blpop 和 brpop 是 lpop 和 rpop 的阻塞版本,和对应⾮阻塞版本的作⽤基本⼀致,除了:

  • 在list不为空的情况下,blpop 和 brpop 就和 lpop 和 rpop作用一样;但如果list为空,blpop 和 brpop就会阻塞住,直到队列不为空
  • 使用blpop 和 brpop 的时候,是可以设置阻塞时间的,如果阻塞了,那么在这个阻塞时间内是可以执行其它命令的;当阻塞时间到了,会自动返回(例子:我约女生晚上6点吃饭,结果我等到9点女生还没来,我就不等了,及时止损,节约我的时间)
  • blpop 华人 brpop 都是可以同时去获取多个key的列表的元素的,就是命令行可以同时出现多个key的,这多个list哪个有元素了,就会返回哪个值(例子:我可以同时约多个女生出来吃饭,哪个女生先到了我就和哪个去吃饭,其她的不管了)
  • 如果多个客⼾端同时多⼀个键执⾏ pop,则最先执⾏命令的客⼾端会得到弹出的元素(例子:多个人约女生,当女生有空时,哪个人最先约的女生,女生就和谁去吃饭)

 2.9 blpop,brpop

blpop key [key ...] timeout
  1. blpop的选项和brpop是一样的,可以指定多个key,每个key都对应一个list,如果这些list有任意一个非空,blpop和brpop都能够把这里的元素获取到立即返回,
  2. 如果list都为空,就会阻塞住,等待其他客户端往list插入数据;
  3. 另外还可以指定超时时间,单位为秒;在Redis 6 版本后,允许设为小数 

我们先针对一个非空的list进行操作:

 

  • 返回的结果是一个pair,一方面告诉我们你当前的数据来自于哪个key,另一方面告诉我们取到的数据是啥

我们针对空的列表进行操作: 

我们删掉key后,在blpop就阻塞住了,然后我们在右边的客户端一创建key, 左边的客户端就立马拿到了数据,brpop作用和现象和blpop一样,就不做演示了

这两个阻塞命令,用途主要就是来作为“消息队列”,但是我们一般不使用,因为前面也说过了,Redis不适合拿来做消息队列,因为有其他更好的产品可以用作消息队列,而且提供的功能也比较多

三,内部编码

旧版本是用ziplist和linkedlist作为底层实现的,但是现在已经不用了,直接用的quicklist来实现了,但是我们去查一些文档时,上面可能还是解释的ziplist和linkedlist,所以我们只简单了解下这两个,重点还是在quicklist:

  • ziplist(压缩列表):当列表的元素个数⼩于 list-max-ziplist-entries 配置(默认 512 个),同时 列表中每个元素的⻓度都⼩于 list-max-ziplist-value 配置(默认 64 字节)时,Redis 会选⽤ ziplist 来作为列表的内部编码实现来减少内存消耗。
  • linkedlist(链表):当列表类型⽆法满⾜ ziplist 的条件时,Redis 会使⽤ linkedlist 作为列表的内 部实现。

quicklist相当于链表和压缩列表的结合,整体是一个链表,但是它的每个节点是一个压缩列表(每个压缩列表都限制大小,然后再把多个压缩列表通过链式结构组织起来,就是quicklist)

四,应用场景

4.1 作为数组

最经典的,就是用list作为“数组”来存储多个元素

4.2 作为消息队列

虽说Redis不经常用来做消息队列,但毕竟是Redis设计的初心,了解一下有利于了解Redis的发展 

多个客户端执行brpop操作,当列表为空时,brpop就会阻塞住,一旦有新元素来了,谁先执行的brpop命令,谁就能拿到这个新来的元素,像这样的设定,就能构成一个“轮询”式的效果。

假设消费者执行的顺序是1 2 3:

  1. 当新元素到达后,首先是消费者1最先执行的brpop,所以它最先拿到元素,然后brpop执行执行完了直接返回,假设线程1还想继续消费,就需要重新执行brpop,重新去排队
  2. 然后再来一个新元素,就是消费者2拿到元素,也从brpop返回,要想再拿数据也需要重新排队
  3. 再来一个新元素,就是消费者3拿到元素了

将上面的模型扩大一下,就是下面这样的了: 

 因为多个 列表或者频道是非常常见的,比如抖音,需要有一个通道来传输视频数据,还有传输弹幕,传输点赞收藏转发,传输评论数据等等,都需要一个频道来传输,因为像这样搞成多个频道,就可以在一个数据通道发生问题时,不会对其他数据造成影响(解耦合

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值