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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/101929.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Spring框架】Spring事务的介绍与使用方法

⚠️ 再提醒一次:Spring 本身并不实现事务,Spring事务 的本质还是底层数据库对事务的支持。你的程序是否支持事务首先取决于数据库 ,比如使用 MySQL 的话,如果你选择的是 innodb 引擎,那么恭喜你,是可以支持…

QT中的按钮控件Buttons介绍

目录 Buttons 按钮控件 1、常用属性介绍 2、按钮介绍 2.1QPushButton 普通按钮 2.2QtoolButton 工具按钮 2.3Radio Button单选按钮 2.4CheckButton复选按钮 2.5Commam Link Button命令链接按钮 2.6Dialog Button Box命令链接按钮 Buttons 按钮控件 在Qt里,…

纯手写Tomcat,看不懂你来揍我【附源码、图文详解】

源码放在了文章末尾 理论知识 何为Tomcat Tomcat是一个开源的Servlet容器,它实现了Java Servlet、JavaServer Pages (JSP)、WebSocket等Java EE规范,用于在Web服务器上运行Java Web应用程序。 说的简单点,Tomcat能处理网络传输来的请求。 …

广告牌安全传感器,实时监测事故隐患尽在掌握

在现代城市中,广告牌作为商业宣传的重要媒介,已然成为城市中一道独特的风景线。然而,随着城市迅速发展,广告牌的安全问题也引起了大众关注。广告招牌一般悬挂于建筑物高处,量大面大。由于设计、材料、施工方法的缺陷&a…

PCIE超高速实时运动控制卡在六面外观视觉检测上的应用

市场应用背景 XPCIE1028超高速实时运动控制卡在六面外观检测高速视觉筛选中的应用,结合正运动技术提供的专用筛选机调试软件,可实现15000pcs/分钟的IO触发检测速度,只需简单参数设置,搭配图像采集硬件和视觉处理软件,…

【算法】活用双指针完成复写零操作

Problem: 1089. 复写零 文章目录 题目解析算法原理分析找到最后一个复写的位置从后往前进行复写操作 代码展示 题目解析 首先我们来分析一下本题的题目意思 可以看到题目中给到了一个数组,意思是让我们将数组中的零元素都复写一遍,然后将其余的元素向后平…

python知识:什么是字符编码?

前言 嗨喽,大家好呀~这里是爱看美女的茜茜呐 我们的MySQL使用latin1的默认字符集, 也就是说,对汉字字段直接使用GBK内码的编码进行存储, 当需要对一些有汉字的字段进行拼音排序时(特别涉及到类似于名字这样的字段时…

图数据库_Neo4j学习cypher语言_使用CQL_构建明星关系图谱_导入明星数据_导入明星关系数据_创建明星关系---Neo4j图数据库工作笔记0009

首先找到明星数据 可以看到有一个sheet1,是,记录了所有的关系的数据 然后比如我们搜索一个撒贝宁,可以看到撒贝宁的数据 然后这个是构建的CQL语句 首先我们先去启动服务 neo4j console 然后我们再来看一下以前导入的,可以看到导入很简单, 就是上面有CQL 看一下节点的属性

Php“牵手”淘宝商品快递费用数据采集方法,淘宝API接口申请指南

淘宝天猫商品快递费用接口 API 是开放平台提供的一种 API 接口,它可以帮助开发者获取商品的详细信息,包括商品的标题、描述、图片,发货地址,快递费用,区域ID,等信息。在电商平台的开发中,快递费…

计算机视觉:比SAM快50倍的分割一切视觉模型FastSAM

目录 引言 1 FastSAM介绍 1.1 FastSAM诞生 1.2 模型算法 1.3 实验结果 2 FastSAM运行环境构建 2.1 conda环境构建 2.2 运行环境安装 2.3 模型下载 3 FastSAM运行 3.1 命令行运行 3.1.1 Everything mode 3.1.2 Text prompt 3.1.3 Box prompt (xywh) 3.1.4 Points p…

springMVC Unix 文件参数变更漏洞修复

错误信息如下: 解决方案: 原因:未对用户输入正确执行危险字符清理 未检查用户输入中是否包含“…”(两个点)字符串,比如 url 为 /login?action…/webapps/RTJEKSWTN26635&typerandomCode cookie为Coo…

opencv 进阶16-基于FAST特征和BRIEF描述符的ORB(图像匹配)

在计算机视觉领域,从图像中提取和匹配特征的能力对于对象识别、图像拼接和相机定位等任务至关重要。实现这一目标的一种流行方法是 ORB(Oriented FAST and Rotated Brief)特征检测器和描述符。ORB 由 Ethan Rublee 等人开发,结合了…

校企合作 | 大势智慧受邀参与北斗共同体建设

8月16日,长江工业职业学院(后简称“长江工院”)副校长刘文胜,质管处处长黄世涛,测绘信息工程系党总支书记刘飞、系副主任陈志兰、系教师陈文玲一行莅临武汉大势智慧科技有限公司(后简称“大势智慧”&#x…

【Git Bash】简明从零教学

目录 Git 的作用官网介绍简明概要 Git 下载链接Git 的初始配置配置用户初始化本地库 Git 状态查询Git 工作机制本地工作机制远端工作机制 Git 的本地管理操作add 将修改添加至暂存区commit 将暂存区提交至本地仓库日志查询版本穿梭 Git 分支查看分支创建与切换分支跨分支修改与…

iPhone开启“轻点唤醒”功能但点击屏幕无反应怎么解决?

iPhone的“轻点唤醒”功能启用时,用户只需手指轻触或点击手机屏幕即可快速唤醒设备,无需按压任何按钮。然而,有些用户在使用“轻点唤醒”功能唤醒屏幕时,遇到该功能失灵,无法正常唤醒屏幕的情况,这是怎么回…

Linux系统安全——NAT(SNAT、DNAT)

目录 NAT SNAT SNAT实际操作 DNAT DNAT实际操作 NAT NAT: network address translation,支持PREROUTING,INPUT,OUTPUT,POSTROUTING四个链 请求报文:修改源/目标IP, 响应报文:修改源/目标…

Yalmip入门教程(5)-约束条件操作的相关函数

博客中所有内容均来源于自己学习过程中积累的经验以及对yalmip官方文档的翻译:https://yalmip.github.io/tutorials/ 这篇博客将详细介绍yalmip工具箱中约束条件操作相关函数的用法。 1.约束条件操作的相关函数 1.1 boundingbox函数 boundingbox函数用于求出一组约…

以软件定义存储实现存力与算力的协同,应对 AI 时代数据挑战

本文根据 XSKY 星辰天合高级副总裁张旭明在“算力与前沿技术创新发展论坛”上的演讲内容整理,略有删节。 算力与前沿技术创新发展论坛以“算力创新跃迁 赋能数字经济”为主题,8 月 17 日在汕头召开,该论坛由工业和信息化部、广东省人民政府主…

SpringBoot 2.7 集成 Netty 4 模拟服务端与客户端通讯入门教程

文章目录 1 摘要2 核心 Maven 依赖3 核心代码3.1 服务端事务处理器 (DemoNettyServerHandler)3.2 服务端连接类(InitNettyServer)3.3 客户端事务处理器(DemoNettyClientHandler)3.4 客户端连接类(DemoNettyClient) 4 测试4.1 测试流程4.2 测试结果4.3 测试结论 5 推荐参考资料6…

通过安全日志读取WFP防火墙放行日志

前言 之前的文档中,描写了如何对WFP防火墙进行操作以及如何在防火墙日志中读取被防火墙拦截网络通讯的日志。这边文档,着重描述如何读取操作系统中所有被放行的网络通信行为。 读取系统中放行的网络通信行为日志,在win10之后的操作系统上&am…