当前位置: 首页 > news >正文

Redis远程字典服务器(5) —— 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拿到元素了

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

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

相关文章:

  • Cocos Creator倒计时
  • jenkins升级踩坑记录
  • service 管理 web 管理插件
  • 电子音乐制作软件有哪些 电音制作用什么软件 好用的能够创作音乐的软件推荐 电音基础新手入门
  • OpenCV--图像梯度处理,图片轮廓,边缘检测
  • 打印一个字符串全部子序列(没有重复字面值)
  • 刷题记录第108天-求一个数的平方根(精确到小数点后五位)
  • 使用 C/C++访问 MySQL
  • repo简介
  • CUDA C++ 编程指南学习(待更)
  • ubuntu16.04安装ibus拼音 输入法
  • 使用功率器件比如MOSFET瞬态热阻曲线计算参数
  • 【myz_tools】Python库 myz_tools:Python算法及文档自动化生成工具
  • 基于NXP IMX6Q+FPGA全自动血液分析仪解决方案
  • 分布式事务和一致性
  • 【391天】每日项目总结系列128(2018.03.03)
  • 【JavaScript】通过闭包创建具有私有属性的实例对象
  • Android单元测试 - 几个重要问题
  • Angular Elements 及其运作原理
  • canvas实际项目操作,包含:线条,圆形,扇形,图片绘制,图片圆角遮罩,矩形,弧形文字...
  • CSS实用技巧
  • js学习笔记
  • Linux gpio口使用方法
  • mockjs让前端开发独立于后端
  • MYSQL如何对数据进行自动化升级--以如果某数据表存在并且某字段不存在时则执行更新操作为例...
  • PHP的类修饰符与访问修饰符
  • SpringCloud集成分布式事务LCN (一)
  • SQLServer插入数据
  • 关于Android中设置闹钟的相对比较完善的解决方案
  • 通信类
  • 学习HTTP相关知识笔记
  • 由插件封装引出的一丢丢思考
  • ​configparser --- 配置文件解析器​
  • ​LeetCode解法汇总1410. HTML 实体解析器
  • ​插件化DPI在商用WIFI中的价值
  • ​一文看懂数据清洗:缺失值、异常值和重复值的处理
  • # Pytorch 中可以直接调用的Loss Functions总结:
  • # 透过事物看本质的能力怎么培养?
  • (C#)一个最简单的链表类
  • (Matlab)遗传算法优化的BP神经网络实现回归预测
  • (SpringBoot)第二章:Spring创建和使用
  • (windows2012共享文件夹和防火墙设置
  • (不用互三)AI绘画工具应该如何选择
  • (精确度,召回率,真阳性,假阳性)ACC、敏感性、特异性等 ROC指标
  • (十八)SpringBoot之发送QQ邮件
  • (十一)c52学习之旅-动态数码管
  • (一)基于IDEA的JAVA基础1
  • **python多态
  • .net Application的目录
  • .Net CF下精确的计时器
  • .NET 跨平台图形库 SkiaSharp 基础应用
  • .NET 依赖注入和配置系统
  • .Net6使用WebSocket与前端进行通信
  • .NET企业级应用架构设计系列之开场白
  • .NET序列化 serializable,反序列化