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

Redis数据结构之List

Redis 中列表(List)类型是用来存储多个有序的字符串,列表中的每个字符串成为元素 Eelement),一个列表最多可以存储 2^32-1 个元素。

在 Redis 中,可以对列表两端插入(push)和弹出(pop),还可以获取指定范围的元素列表、获取指定索引下标的元素等。列表是一种比较灵活的数据结构,可以充当栈和队列的角色,在实际开发中有很多应用场景。


文章目录

        • 1、List数据类型
          • 1.1、List类型简介
          • 1.2、List应用场景
        • 2、List底层结构
          • 2.1、List底层结构介绍
          • 2.2、压缩列表ZipList
          • 2.3、双向链表LinkedList
          • 2.4、快速链表QucikList
        • 3、List常用命令
          • 3.1、将新值加入列表头部
          • 3.2、将新值加入列表尾部
          • 3.3、获取列表中某区间的值
          • 3.4、移除列表中头部的值,并返回此值
          • 3.5、移除列表中尾部的值,并返回此值
          • 3.6、通过下标获取列表中的值
          • 3.7、 删除指定值及数量的元素值
          • 3.8、得到列表长度
          • 3.9、截断列表
          • 3.10、将值从一个列表移动到另一个列表
          • 3.11、替换列表中某个值
          • 3.12、指定位置将新值插入列表


1、List数据类型

1.1、List类型简介

Redis 中列表(List)类型是用来存储多个有序的字符串,列表中的每个字符串成为元素 Eelement),一个列表最多可以存储 2^32-1 个元素。

在 Redis 中,可以对列表两端插入(push)和弹出(pop),还可以获取指定范围的元素列表、获取指定索引下标的元素等。列表是一种比较灵活的数据结构,可以充当栈和队列的角色,在实际开发中有很多应用场景。

列表类型有以下特点:

  • 列表中的元素是有序的,即可以通过索引下标获取某个元素或者某个范围内的元素列表;

  • 列表中的元素可以是重复的

1.2、List应用场景

根据 Redis 双向列表的特性,因此其也被用于异步队列的使用。实际开发中将需要延后处理的任务结构体序列化成字符串,放入 Redis 的队列中,另一个线程从这个列表中获取数据进行后续处理。

使用场景:

  • 消息队列:消息队列在存取消息时,必须要满足三个需求,分别是消息保序、处理重复的消息和保证消息可靠性。Redis 的 List 和 Stream 两种数据类型,就可以满足消息队列的这三个需求;
  • 最新消息排队功能:与消息队列类似

但另一方面,使用 Redis List 作为消息队列也有一些不足,比如:

  • 消息持久化 :Redis 是内存数据库,虽然有 Aof 和 Rdb 两种机制进行持久化,但这只是辅助手段,这两种手段都是不可靠的。当 Redis 服务器宕机时一定会丢失一部分数据,这对于很多业务都是没法接受的;
  • 热 Key 性能问题:不论是用 Codis 还是 Twemproxy 这种集群方案,对某个队列的读写请求最终都会落到同一台 Redis 实例上,并且无法通过扩容来解决问题。如果对某个 List 的并发读写非常高,就产生了无法解决的热 Key,严重可能导致系统崩溃;
  • 每当执行 Rpop 消费一条数据,那条消息就被从 List 中永久删除了。如果消费者消费失败,这条消息也没法找回了。你可能说消费者可以在失败时把这条消息重新投递到进队列,但这太理想了,极端一点万一消费者进程直接崩了呢,比如被 kill -9,panic,coredump…
  • 一条消息只能被一个消费者消费,Rpop 之后就没了。如果队列中存储的是应用的日志,对于同一条消息,监控系统需要消费它来进行可能的报警,BI 系统需要消费它来绘制报表,链路追踪需要消费它来绘制调用关系……这种场景 Redis List 就没办法支持了;
  • 不支持二次消费 :一条消息 Rpop 之后就没了。如果消费者程序运行到一半发现代码有 Bug,修复之后想从头再消费一次就不行了

对于上述的不足,目前看来第一条(持久化)是可以解决的。很多公司都有团队基于 Rocksdb Leveldb 进行二次开发,实现了支持 Redis 协议的 kv 存储。这些存储已经不是 Redis 了,但是用起来和 Redis 几乎一样。它们能够保证数据的持久化,但对于上述的其他缺陷也无能为力了。

其实 Redis 5.0 开始新增了一个 Stream 数据类型,它是专门设计成为消息队列的数据结构,借鉴了很多 Kafka 的设计,但是依然还有很多问题…直接进入到kafka的世界它不香吗?


2、List底层结构

2.1、List底层结构介绍

在 Redis3.2 版本前,Redis 列表 List 使用两种数据结构作为底层实现:

  • 压缩列表 ZipList:插入元素过多或字符串太大,就需要调用 Realloc 扩展内存;
  • 双向链表 LinkedList:需附加指针 Prev 和 Next,较浪费空间,加重内存的碎片化

Redis3.2 首先以 ZipList 进行存储,在不满足 ZipList 的存储要求后转换为 LinkedList 列表。当列表对象同时满足以下两个条件时,列表对象使用 ZipList 进行存储,否则用 LinkedList 存储。

  • 列表对象保存的所有字符串元素的长度小于 64 字节;
  • 列表对象保存的元素数量小于 512 个

在 Redis3.2 版本后,Redis 列表使用 快速链表 QucikList 结构作为底层实现。

2.2、压缩列表ZipList

在 Redis3.2 版本前压缩列表不仅是 List 的底层实现之一,同时也是 Hash、 ZSet 两种数据类型底层实现之一。

压缩列表是一块连续的内存空间 (像内存连续的数组,但每个元素长度不同),一个 ziplist 可以包含多个节点(entry)。元素之间紧挨着存储,没有任何冗余空隙。

image-20230813151525090

压缩列表的本质就是一个数组,只不过是增加了 “列表长度”、“尾部偏移量”、“列表元素个数” 以及 “列表结束标识”,这样的话就有利于快速的寻找列表的首、尾节点.压缩列表将表中每一项存放在前后连续的地址空间内,每一项因占用的空间不同,而采用变长编码。由于内存是连续分配的,所以遍历速度很快。

当我们的 List 列表数据量比较少的时候,且存储的数据轻量的(如小整数值、短字符串)时候, Redis 就会通过压缩列表来进行底层实现。

+--------+--------+--------+-------+----+--------+-------+
| zlbytes| zltail | zllen | entry1 | .. | entryN | zlend |
+--------+--------+--------+-------+----+--------+-------+
属性说明
“zlbytes”表示压缩列表的长度(包括所有的字节)
“zltail”表示压缩列表尾部的指针(偏移量)
“zllen”表示压缩列表中节点(Entry)的个数
“entry”存储区,可以包含多个节点,每个节点可以存放整数或者字符串
“zlend”表示列表结束

如果查找定位首个元素或最后1个元素,可以通过表头 “zlbytes”、“zltail_offset” 元素快速获取,复杂度是 O(1)。但是查找其他元素时,就没有这么高效了,只能逐个查找下去,比如 entryN 的复杂度就是 O(N)。

2.3、双向链表LinkedList

LinkedList 是标准的双向链表,Node 节点包含 prev 和 next 指针,分别指向后继与前驱节点,因此从双向链表中的任意一个节点开始都可以很方便地访问其前驱与后继节点。

image-20230820203741187

LinkedList 可以进行双向遍历;添加删除元素快 O(1),查找元素慢 O(n),高效实现了 LPUSH 、RPOP、RPOPLPUSH,但由于需要为每个节点分配额外的内存空间,所以会浪费一定的内存空间。这种编码方式适用于元素数量较多或者元素较大的场景。

LinkedList 结构为链表提供了表头指针 head、表尾指针 tail,以及节点数量计算 len。下图展示一个由 list 结构和三个 listNode 节点组成的链表:

image-20230820204233474

Redis 的链表实现的特性可以总结如下:

  • 双端:链表节点带有 prev 和 next 指针,获取某个节点的前一节点和后一节点的复杂度都是 O(1);
  • 无环:表头节点的 prev 指针和表尾节点的 next 指针都指向 NULL,对链表的访问以 NULL 为终点;
  • 表头指针/表尾指针:通过 list 结构的 head 指针和 tail 指针,获取链表的表头节点和表尾节点的复杂度为 O(1);
  • 链表长度计数器:通过 list 结构的 len 属性来对 list 的链表节点进行计数,获取节点数量的复杂度为O(1);
  • 多态:链表节点使用 void* 指针来保存节点值,并通过 list 结构的 dup、free、match 三个属性为节点值设置类型特定函数,所以链表可以用于保存各种不同类型的值。
    使用链表的附加空间相对太高,因为 64bit 系统中指针是 8 个字节,所以 prev 和 next 指针需要占据 16 个字节,且链表节点在内存中单独分配,会加剧内存的碎片化,影响内存管理效率
2.4、快速链表QucikList

Redis3.2 版本开始,List 类型数据使用的底层数据结构是快速链表,快速列表是以压缩列表为节点的双向链表,将双向链表按段切分,每一段使用压缩列表进行内存的连续存储,多个压缩列表通过 prev 和 next 指针组成的双向链

image

考虑到链表的以上缺点,Redis 后续版本对列表数据结构进行改造,使用 QucikList 代替了 ZipList 和 LinkedList。 作为 ZipList 和 LinkedList 的混合体,它将 LinkedList 按段切分,每一段使用 ZipList 来紧凑存储,多个 ZipList 之间使用双向指针串接起来。

这样,性能就的得到了更大的提升。

说明
“head”表示快速链表的头部节点
“tail”表示快速链表的尾部节点
“count”表示快速链表中所有节点中元素的总数
“len”表示快速链表中节点的个数
“fill”ziplist 节点的最大大小,值默认 8kb,大小超出后会新建一个 Ziplist,对应 list-max-ziplist-size 参数,占 16bit。
“compress”节点压缩深度,表示节点是否使用 LZF 算法压缩,对应 list-compress-depth 参数,占1 6bit

对于 “fill” 当数字为负数:

  • -1:每个 ZipList 节点大小不能超过 4kb(建议)
  • -2:每个 ZipList 节点大小不能超过 8kb(默认配置)
  • -3:每个 ZipList 节点大小不能超过 16kb(一般不建议)
  • -4:每个 ZipList 节点大小不能超过 32kb(不建议)
  • -5:每个 ZipList 节点大小不能超过 64kb(正常工作量不建议)

对于 “fill” 当数字为正数:ZipList 节点最多包含的元素个数,最大值为 215215

对于 “compress” 节点,数字含义如下:

  • 0:不压缩(默认)

  • 1:QucikList 列表的两端各有1个ziplist节点不压缩,中间的节点压缩

  • 2:QucikList 列表的两端各有2个ziplist节点不压缩,中间的节点压缩

  • 3:QucikList 列表的两端各有3个ziplist节点不压缩,中间的节点压缩

  • 以此类推,最大为 216216


3、List常用命令

3.1、将新值加入列表头部

使用 LPUSH 命令将新值加入列表头部:

LPUSH list value [value2 ...]

将一个或多个值插入到列表头部。如果 key 值不存在,会先创建再执行 LPUSH 命令,如果 key 值存在但不是列表类型时,返回一个错误。

image-20230820225102203

3.2、将新值加入列表尾部

使用 RPUSH 命令将新值加入列表尾部:

RPUSH list value [value2 ...]

将一个或多个值插入到列表尾部。如果 key 值不存在,会先创建再执行 LPUSH 命令,如果 key 值存在但不是列表类型时,返回一个错误

image-20230820225151979

3.3、获取列表中某区间的值

使用 LRANGE 命令获取列表中某区间的值:

LRANGE 列表名 start end

获取列表中指定区间的元素,0 表示列表中第一个元素,-1 表示列表中最后一个元素

image-20230820225428723

3.4、移除列表中头部的值,并返回此值

使用 LPOP 命令移除列表中头部的值,并返回此值:

LPOP list

image-20230820230119496

3.5、移除列表中尾部的值,并返回此值

使用 RPOP 命令移除列表中尾部的值,并返回此值:

RPOP list

image-20230820230216229

3.6、通过下标获取列表中的值

使用 LINDEX 通过下标获取列表中的值:

LINDEX list index

image-20230820230507171

3.7、 删除指定值及数量的元素值

使用 LREM 删除指定值及数量的元素值:

LREM list num value

image-20230820231024739

3.8、得到列表长度

使用 LLEN 得到列表长度

llen list

image-20230820231136297

3.9、截断列表

使用 LTRIM 截断列表

LTRIM list start end

image-20230820231328091

3.10、将值从一个列表移动到另一个列表

使用 RPOPLPUSH 将值从一个列表移动到另一个列表

RPOPLPUSH source distination

将 source 列表中最后一个元素移除,并将该元素添加到 destination 列表中,可简单理解为 “尾删头插”

image-20230820231857323

3.11、替换列表中某个值

使用 LSET 替换列表中某个值

LSET list index value

image-20230820232028273

3.12、指定位置将新值插入列表

使用 LINSERT 指定位置将新值插入列表

LINSERT list BEFORE / AFTER old-value new-value

image-20230820232320044

相关文章:

Redis数据结构之List

Redis 中列表(List)类型是用来存储多个有序的字符串,列表中的每个字符串成为元素 Eelement),一个列表最多可以存储 2^32-1 个元素。 在 Redis 中,可以对列表两端插入(push)和弹出&am…...

SpringCloud Alibaba实战和源码(7)Skywalking

什么是SkyWalking Skywalking是由国内开源爱好者吴晟开源并提交到Apache孵化器的产品,它同时吸收了Zipkin /Pinpoint /CAT 的设计思路。特点是:支持多种插件,UI功能较强,支持非侵入式埋点。目前使用厂商最多,版本更新较…...

MySQL索引可能失效之or、is null、is not null、不等于(!=,<>)、联合索引

1、如果 A,B 两列都有索引,那么 select * from Table where Aa or Bb; 会走索引吗? 答案:会,因为 A,B都有索引; 2、如果 A,B有索引,但是C没有索引; select * from Table where Aa or Bb …...

无人机电力巡检:探索电力设施维护的新模式

电力巡检一直是电力行业中关键的环节,它的目的是确保电力设施的正常运行和安全稳定,对提高电力设施的可靠性、确保电力供应的稳定性和提高电力企业的管理水平具有重要的意义。传统的电力巡检方式通常采用人工的方式进行,这种方式存在很多的问…...

ethers.js1:ethers的安装和使用

ethers官方文档:Documentation 1、ethers简介: ethers.js是一个完整而紧凑的开源库,用于与以太坊区块链及其生态系统进行交互。如果你要写Dapp的前端,你就需要用到ethers.js。 与更早出现的web3.js相比,它有以下优点…...

小程序中的页面配置和网络数据请求

页面配置文件和常用的配置项 1.在msg.json中配置window中的颜色和背景色 "navigationBarBackgroundColor": "#efefef","navigationBarTextStyle": "black" 2.可以看到home中的没有发生变化但是msg的发生变化了,这个和前面的…...

使用ImageMagick实现多张图片拼接为gif(多线程版)

官网: https://imagemagick.org/ 直接上代码 ExecutorService es Executors.newFixedThreadPool(10); List<File> images getImageFiles(sceneDir); CountDownLatch cdl new CountDownLatch(images.size()); // 拷贝图片 for (File file : images) {System.out.prin…...

解释 RESTful API,以及如何使用它构建 web 应用程序。

RESTful API是一种利用HTTP协议进行通信的Web API设计风格&#xff0c;它采用了一组统一且可缓存的操作&#xff0c;包括GET、POST、PUT、DELETE等&#xff0c;通过URL来定位资源&#xff0c;以及使用JSON、XML等格式来传输数据&#xff0c;以实现系统之间的数据交互和资源共享…...

远程端口转发 实践 如何将物理机某一端口的服务转发到vps上,使得外网能访问到

以本机1470端口&#xff08;我的sqli-labs&#xff09;与vps的9023端口为例。 SSH基本的连接命令是&#xff1a; ssh usernamehostname这里牵扯到了两台主机&#xff0c;一是执行命令、运行SSH客户端的主机&#xff0c;我们称为本地主机A【Host A】&#xff1b;二是接收连接请…...

【uniapp 监听键盘弹起与收回】

在uniapp中&#xff0c;可以通过使用小程序提供的API来监听键盘弹起与收回。 首先&#xff0c;在页面的onLoad函数中注册监听事件&#xff1a; onLoad() {uni.onKeyboardHeightChange(this.onKeyboardHeightChange); },然后&#xff0c;在页面的onUnload函数中取消注册监听事…...

【Unity】如何制作小地图

我们为什么要制作小地图呢&#xff1f; 原因很简单&#xff1a; 导航和定位&#xff1a;小地图可以显示玩家当前位置以及周围环境的概览。这使得玩家能够更好地导航和定位自己在游戏中的位置&#xff0c;找到目标或避开障碍物。场景了解&#xff1a;通过小地图&#xff0c;玩…...

基于IMX6ULLmini的linux裸机开发系列八:按键处理实验

目录 GIC相关寄存器 GPIO中断相关寄存器 中断服务函数表 中断向量表偏移位置 make有报错 解决方法&#xff1a;error: for loop initial declarations are only allowed in C99 mode_‘for’ loop initial declarations are only allowed i_Young_2717的博客-CSDN博客 GIC…...

数据结构好题总结

Cut Inequality Down 题解 https://blog.csdn.net/lzh_naive/article/details/103340568 概括&#xff1a;st表倍增类st表 考虑如果没有UL限制的话&#xff0c;相当于是前缀和 我们发现&#xff0c;如果某次到了U/L&#xff08;相当于是一次碰壁&#xff09;那么这个值已知…...

Java串口开发

网上搜索了关于java串口开发的资料,发现都不是特别的全,故写下一些心得以帮助其他人能快速上手java串口开发,如有错漏之处&#xff0c;敬请指正 串口开发会用到一个javax.comm和RXTXcomm库,&#xff0c;javax.comm库不支持64位操作系统。该库仅适用于32位操作系统,所以接下来主…...

Python nohup 启动python脚本,后台没有日志

一、情况 1.linux上运行python脚本&#xff0c;前台运行打印日志&#xff0c;后台使用nohup不打印日志。 前台运行 ./xxx.py 后台运行 nohup python ./xxx.py > xxx.log 2>&1 &二、排查思路 2.1 脚本是否有问题 首先看自己写的python脚本是否存在问题。因为…...

完美解决微信小程序使用复选框van-checkbox无法选中

由于小程序使用了vant-ui框架&#xff0c;导致checkbox点击无法选中问题 <van-checkbox value"{{ checked }}" shape"square"><view class"check-content"><view class"checktext">我已阅读并同意>《用户协议》…...

IDEA报错:类文件具有错误的版本 61.0,应为52.0

springboot项目启动报错&#xff1a; 类文件具有错误的版本 61.0,应为52.0 请删除该文件或确保该文件位于正确的类路径子目录中 查阅了网上的很多资料&#xff0c;普遍原因说是springboot版本过高&#xff0c;高于3.0 需要在pom文件中降低版本 也有说是idea的maven配置java版…...

Linux 挂载局域网内共享目录

Linux 挂载局域网内共享目录 1、安装samba服务端2、samba服务端配置3、添加samba服务访问账户4、防火墙5、重启服务6、windows访问7、linux访问 1、安装samba服务端 sudo apt-get install -y samba yum install -y samba2、samba服务端配置 vim /etc/samba/smb.conf在文档尾部…...

FFmpeg解码32k大分辨率出现如下错误:Picture size 32768x32768 is invalid

最近找到一张32k的jpeg图片&#xff0c;尝试用ffmpeg来进行解码&#xff0c;命令如下&#xff1a; ffmpeg -i enflame_32768-32768-420.jpg 32.yuv结果出现Picture size 32768x32768 is invalid的错误&#xff1a; 找到报错的代码文件imgutils.c&#xff0c;以及函数&#x…...

EasyExcel+POI制作带有有效性校验及下拉联动的Excel模板

文章目录 1.背景2.实现功能的Excel特性2.1.特性介绍2.2.下拉框联动2.3.单元格自动匹配Id2.4.错误提示 3.代码实现3.1.基础流程代码3.2.名称管理器配置3.3.有效性配置3.4.函数填充3.5.其他补充 4.总结 1.背景 最近在做一个CRM系统的人员销售目标导入的相关需求&#xff0c;需要…...

dedecms 织梦自定义表单留言增加ajax验证码功能

增加ajax功能模块&#xff0c;用户不点击提交按钮&#xff0c;只要输入框失去焦点&#xff0c;就会提前提示验证码是否正确。 一&#xff0c;模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...

【Web 进阶篇】优雅的接口设计:统一响应、全局异常处理与参数校验

系列回顾&#xff1a; 在上一篇中&#xff0c;我们成功地为应用集成了数据库&#xff0c;并使用 Spring Data JPA 实现了基本的 CRUD API。我们的应用现在能“记忆”数据了&#xff01;但是&#xff0c;如果你仔细审视那些 API&#xff0c;会发现它们还很“粗糙”&#xff1a;有…...

sipsak:SIP瑞士军刀!全参数详细教程!Kali Linux教程!

简介 sipsak 是一个面向会话初始协议 (SIP) 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求&#xff0c;并检查收到的响应。它以以下模式之一…...

Scrapy-Redis分布式爬虫架构的可扩展性与容错性增强:基于微服务与容器化的解决方案

在大数据时代&#xff0c;海量数据的采集与处理成为企业和研究机构获取信息的关键环节。Scrapy-Redis作为一种经典的分布式爬虫架构&#xff0c;在处理大规模数据抓取任务时展现出强大的能力。然而&#xff0c;随着业务规模的不断扩大和数据抓取需求的日益复杂&#xff0c;传统…...

如何应对敏捷转型中的团队阻力

应对敏捷转型中的团队阻力需要明确沟通敏捷转型目的、提升团队参与感、提供充分的培训与支持、逐步推进敏捷实践、建立清晰的奖励和反馈机制。其中&#xff0c;明确沟通敏捷转型目的尤为关键&#xff0c;团队成员只有清晰理解转型背后的原因和利益&#xff0c;才能降低对变化的…...

通过MicroSip配置自己的freeswitch服务器进行调试记录

之前用docker安装的freeswitch的&#xff0c;启动是正常的&#xff0c; 但用下面的Microsip连接不上 主要原因有可能一下几个 1、通过下面命令可以看 [rootlocalhost default]# docker exec -it freeswitch fs_cli -x "sofia status profile internal"Name …...

向量几何的二元性:叉乘模长与内积投影的深层联系

在数学与物理的空间世界中&#xff0c;向量运算构成了理解几何结构的基石。叉乘&#xff08;外积&#xff09;与点积&#xff08;内积&#xff09;作为向量代数的两大支柱&#xff0c;表面上呈现出截然不同的几何意义与代数形式&#xff0c;却在深层次上揭示了向量间相互作用的…...

用神经网络读懂你的“心情”:揭秘情绪识别系统背后的AI魔法

用神经网络读懂你的“心情”:揭秘情绪识别系统背后的AI魔法 大家好,我是Echo_Wish。最近刷短视频、看直播,有没有发现,越来越多的应用都开始“懂你”了——它们能感知你的情绪,推荐更合适的内容,甚至帮客服识别用户情绪,提升服务体验。这背后,神经网络在悄悄发力,撑起…...

从零手写Java版本的LSM Tree (一):LSM Tree 概述

&#x1f525; 推荐一个高质量的Java LSM Tree开源项目&#xff01; https://github.com/brianxiadong/java-lsm-tree java-lsm-tree 是一个从零实现的Log-Structured Merge Tree&#xff0c;专为高并发写入场景设计。 核心亮点&#xff1a; ⚡ 极致性能&#xff1a;写入速度超…...

Linux 内存管理调试分析:ftrace、perf、crash 的系统化使用

Linux 内存管理调试分析&#xff1a;ftrace、perf、crash 的系统化使用 Linux 内核内存管理是构成整个内核性能和系统稳定性的基础&#xff0c;但这一子系统结构复杂&#xff0c;常常有设置失败、性能展示不良、OOM 杀进程等问题。要分析这些问题&#xff0c;需要一套工具化、…...