四、Redis五种常用数据类型-List

List是Redis中的列表,按照插入顺序保存数据,插入顺序是什么样的,数据就怎么保存。可以添加一个元素到列表的头部(左边)或者尾部(右边)。一个列表最多可以包含232-1个元素(4294967295,每个列表超过40亿个元素)。是一种双向列表结构。

1、List列表命令

  • blpop key1[key2…] timeout:从头部(左边)移出并获取一个元素,如果列表没有元素会阻塞到列表有元素或者超时。
  • brpop key1[key2…] timeout:从尾部(右边)移出并获取一个元素,如果列表没有元素会阻塞到列表有元素或者超时。
  • brpoplpush source destination timeout:从尾部(右边)移出一个元素,并添加到另一个列表的头部并返回。如果列表没有元素会阻塞到列表有元素或者超时。
  • lindex key index:通过索引(下标)获取列表中的元素。index可以为负,-1表示最后一个元素,-2表示倒数第二个元素。以此类推
  • linsert key before|after pivot value:将值value插入到列表key中,位于pivot之前或者之后。

当指定的元素不在列表或者列表为空时不执行任何操作,如果key不是列表类型,则返回一个错误

  • llen key:获取列表的长度
  • lpop key:从列表的头部(左边)移出并获取列表一个元素。如果列表没有元素,不会阻塞。
  • lpush key value1[value2]:将一个或者多个值插入到列表头部(左边)。多个元素一起插入时,后面的头部。

如果插入的key不存在,将创建一个空的列表,并执行lpush操作。当key不是列表类型时,会报错。

  • lpushx key value:将一个值插入到列表的头部(左边),当列表不存在时操作无效。
  • lrange key start end:返回列表中指定区间的元素,从0开始,当end为-1时表示到最后一个元素,-2为倒数第二个元素。
  • lren key count value:移出列表中值和value相等的值。

count的值有如下几种情况:

  • count>0:从列表的头部(左)到尾部(右)搜索,移出值与value相等的元素,数量为count。
  • count<0:从列表的尾部(右)到头部(做)搜索,移出值与value相等的元素,数量为count的绝对值。
  • count=0:移出列表中所有与value相等的元素。
  • lset key index value:通过索引(下标)设置元素的值。当下标超出范围(超过列表的已有数量)或者列表为空,进行lset操作会报错。
  • ltrim key start end:对一个列表进行修剪,让列表只存在指定区间内的元素,以外的全删除。0表示第一个元素,1表示第二个,-1表示最后一个,-2表示倒数第二个。以此类推。
  • rpop key:移出列表的最后一个元素(最右边)。不会阻塞。
  • rpoplpush source destination:移出source列表的最后一个元素,插入到destination的头部,并返回。
  • rpush key value1[value2]:在列表尾部添加一个或者多个元素。后边的在最后。
  • roushx key value:将值添加到已存在列表的尾部。

2、底层结构

2.1、linkedList

Java中的LinkedList类似,Redis中的linkedList是一个双向链表,也是由一个个的节点组成。Redis借助c语言的链表节点结构如下:

typedf struct listNode{//前一个节点struct listNode *prev;//后一个节点struct listNode *next;//当前节点的值的指针void *value;
}listNode;

prev指向前一个节点,next指向下一个节点,value保存着当前节点对应的数据对象。listNode结构示意图如下:
image.png
链表的结构如下:

typedf struct list{//头指针listNode *head;//尾指针listNode *tail;//节点拷贝函数void *(*dup)(void *ptr);//释放节点函数void *(*free)(void *ptr);//判断两个节点是否相等函数int (*match)(void *ptr,void *key);//链表长度unsigned long len;
}list;

head指向链表的头结点,tail指向链表的尾节点。len表示链表有多少个节点,这样可以在O(1)的时间复杂度内获得链表的长度。
链表的结构示意图如下:
image.png

2.2、zipList

Redis的zipList结构如下:

typedf struct ziplist<T>{//压缩列表占用字符数int32 zlbytes;//最后一个元素距离起始位置的偏移量,用于快速定位最后一个节点int32 zltail_offset;//元素个数int16 zllength;//元素内容T[] entries;//结束位 0xFFint8 zlend;
}ziplist

zipList结构示意图如下:
image.png
根据zltail_offset字段就可以快速定位到最后一个entry的位置,这样配合Entry结构中的prelen字段,就可以实现链表的倒序遍历。
下面是entry的结构:

typed struct entry{//前一个entry的长度int<var> prelen;//元素编码类型int<var> encoding;//元素内容optional byte[] content;
}entry

prelen保存了前一个entry的长度,这样就可以在倒序遍历时就可以根据此字段来获取到前一个entry的位置。encoding保存了content内容的编码类型。content是保存的内容,其类型是optional,表示这个字段是可选的。当content是很小的整数时,他会内连到content的尾部。entry结构示意图如下:
image.png
思考题:为什么有了**linkedList**还要设计一个**zipList**呢?

  • 就像zipList的名字一样,他是一个压缩列表,相比较于linkedList,其少了prenext两个指针。Redis中两个指针就要占用16个字节(64位操作系统的一个指针就是8字节)。
  • linkedList每个节点的内存都是单独分配,加速了内存的碎片化,影响内存的管理。
  • zipList的内存都是连续组成的,这样一来,由于内存是连续就减少了许多内存的碎片化和指针的内存占用。

zipList遍历时,先根据zlbyteszltail_offset定位到最后一个entry的位置,然后根据最后一个entryprelen确定前一个entry的位置。

连锁更新

前面说到,Entry结构中有个prelen字段,表示其前一个entry的长度,他的长度要么是1个字节,要么是5个字节:

  • 前一个entry的长度小于254字节,则prelen的长度为1字节(8位,最大可表示255);
  • 前一个entry的长度大于254字节,则prelen的长度为5字节;

假设有一组压缩列表的长度都在250~253之间,突然增加一个entry长度大于254字节。由于新的entry的长度大于254字节,那么原链表的首节点的prelen的长度就会为5个字节,随后会导致其他的所有entryprelen都增大为5个字节,每次prelen的增大,都需要一次内存的再分配操作。
image.png

linkedList和zipList的对比
  • 当列表的长度或者数量较少时,通常采用zipList,当列表的长度较大或者数量较多时,通常采用linkedList存储。
  • 双向链表linkedList方便与在列表上增加或者删除操作,也即pushpop操作。但是其内存的开销比较大。一方面其比zipList多了两个指针。且其每个节点的内存都是独立的,地址不连续,容易形成内存碎片。
  • zipList存储在一块连续的内存上,其查询速度较快,但是不利于修改操作,插入和删除操作需要频繁的申请和释放内存。特别是当zipList很大时,一次realloc可能会导致大量的拷贝。

2.3、quickList

Redis3.2版本后,list的底层实现又多了一种,quickListquickListzipListlinkedList的混合体,它将linkedList按段切分,每一段使用zipList来存储数据。每个quickList之间用双向指针串接起来。示意图如下:
image.png
节点quickList结构如下:

typedf struct quicklistNode{//前一个节点quicklistNode *prev;//后一个节点quicklistNode *next;//压缩列表ziplist* zl;//ziplist大小int32 size;//ziplist中元素数量int16 count;//编码形式 存储ziplist还是LZF压缩存的ziplistint2 encoding;...
}quicklistNode;

quickList的结构如下:

typedf struct quicklist{//头节点quicklistNode* head;//尾节点quicklistNode* tail;//元素总数long count;//quicklistNode节点总数int nodes;//压缩深度算法int compressDepth;...
}quicklist;

为了进一步节约空间,Redis还会对ziplist进行压缩存储,使用LZF算法进行压缩,可以选择压缩深度。

每个zipList可以存储元素的个数

Redis.conf文件,在DVANCE CONFIG下面有着清晰记载:

# Lists are also encoded in a special way to save a lot of space.
# The number of entries allowed per internal list node can be specified
# as a fixed maximum size or a maximum number of elements.
# For a fixed maximum size, use -5 through -1, meaning:
# -5: max size: 64 Kb  <-- not recommended for normal workloads
# -4: max size: 32 Kb  <-- not recommended
# -3: max size: 16 Kb  <-- probably not recommended
# -2: max size: 8 Kb   <-- good
# -1: max size: 4 Kb   <-- good
# Positive numbers mean store up to _exactly_ that number of elements
# per list node.
# The highest performing option is usually -2 (8 Kb size) or -1 (4 Kb size),
# but if your use case is unique, adjust the settings as necessary.
list-max-ziplist-size -2

quicklist内部默认单个ziplist的长度为8K字节,即list-max-ziplist-size的值设置为-2,超出这个阈值,就会重新生成一个新的ziplist来存储数据。根据注释可知,性能最好的时候就是list-max-ziplist-size为-1和-2时,即分别是4Kb和8Kb。当然这个值也可以是正数,当值为正数n时,表示每个quicklist上的ziplist最多包含n个数据项。

压缩深度

quickList中可以使用压缩算法对zipList进行进一步的压缩,这个算法就是LZF算法,这是一种无损压缩算法。使用压缩算法对zipList进行压缩后,zipList结构如下:

typedf struct zoplist_compressed{//元素个数int32 size;//元素内容byte[] compressed_data;
}

压缩后的quickList结构如下:
image.png
Redis.conf配置文件中对其进行配置

# Lists may also be compressed.
# Compress depth is the number of quicklist ziplist nodes from *each* side of
# the list to *exclude* from compression.  The head and tail of the list
# are always uncompressed for fast push/pop operations.  Settings are:
# 0: disable all list compression
# 1: depth 1 means "don't start compressing until after 1 node into the list,
#    going from either the head or tail"
#    So: [head]->node->node->...->node->[tail]
#    [head], [tail] will always be uncompressed; inner nodes will compress.
# 2: [head]->[next]->node->node->...->node->[prev]->[tail]
#    2 here means: don't compress head or head->next or tail->prev or tail,
#    but compress all nodes between them.
# 3: [head]->[next]->[next]->node->node->...->node->[prev]->[prev]->[tail]
# etc.
list-compress-depth 0

list-compress-depth这个参数表示一quickList**两端不被压缩的节点个数,**实际上这里的节点个数指的是quickList的节点个数,而不是zipList里面的数据项个数。

  • quickList默认的压缩深度为0,也就是不开启压缩。
  • list-compress-depth为1时,表示首尾各有一个节点不进行压缩,中间节点进行压缩。
  • list-compress-depth为2时,表示首尾各有两个节点不行压缩,中间节点进行压缩。
  • 以此类推。

从上面可以看到quickList的首尾两个节点永远不会被压缩。

3、总结

image.png

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

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

相关文章

Redis单机安装

1.编译 cd redis安装目录 makemake install2.修改配置文件redis.conf #端口修改 port 6379 #后台进程启动 yes daemonize yes # daemonize no #注释掉 为了可以远程连接 #bind 127.0.0.1 #设置密码 requirepass pwd3.启动 ./redis-server ../redis.conf查看进程 [rootlocal…

使用Maven对Java独立应用程序进行编译打包

一、 安装Maven 1.解压&#xff0c;移动安装包 sudo tar -zxf ~/apache-maven-3.9.6-bin.tar.gz -C /usr/local/ cd /usr/local/ sudo mv apache-maven-3.9.6/ ./maven-3.9.6 sudo chown -R qiangzi ./maven-3.9.6 二、Java应用程序代码 1.版本信息&#xff1a; Spark-2.1…

设计软件有哪些?渲染软件篇(1),渲染100邀请码1a12

做设计要用到很多软件&#xff0c;今天我给大家介绍一些渲染方面的&#xff0c;仅供参考。 1、渲染100(http://www.xuanran100.com/?ycode1a12) 渲染100是网渲平台&#xff0c;为设计师提供高性能的渲染服务。通过它设计师可以把本地渲染移到云端进行&#xff0c;速度快价格便…

APP广告变现:自刷的秘密与规则

在移动互联网时代&#xff0c;广告已成为众多APP盈利的主要方式之一。对于开发者和运营者而言&#xff0c;如何通过广告变现提高收益是他们必须关注的问题。然而&#xff0c;在众多的变现方法中&#xff0c;“自刷广告”这一概念可能让一些人感到迷惑。实际上&#xff0c;只要在…

巨资回流,量子投资热潮再起

一股新的信心和资金浪潮正在席卷量子计算产业。 2023年4月30日&#xff0c;澳大利亚联邦政府和昆士兰州政府宣布共同出资9.4亿澳元&#xff08;约合6.2亿美元&#xff09;&#xff0c;支持美国初创企业PsiQuantum在布里斯班附近建设一台大型量子计算机。这项投资是最新的迹象之…

字符串的赋值

因为字符串本质上就是数组&#xff0c;因此它的赋值也具有数组的特点&#xff1a;只能在初始化时用等号。 1&#xff0e;字符串的初始化 方法一&#xff1a;指定大小 char s[6]"Hello"; //指定字符串最大占用6个的char型内存单元 虽然看上去是将整个字符串赋给了…

RT-IoT2022 数据集-扩展数据(自制方法)

数据集官网Discover datasets around the world!https://archive.ics.uci.edu/dataset/942/rt-iot2022RT-IoT2022 是源自实时物联网基础设施的专有数据集&#xff0c;作为集成了各种物联网设备和复杂网络攻击方法的综合资源而引入。该数据集包含正常和对抗性网络行为&#xff0…

基于ConvNeXt网络的图像识别

1、前言 ConvNeXt 网络基于传统的卷积神经网络&#xff0c;与当下 transformer当道而言简直是一股清流 ConvNeXt并没有特别复杂或者创新的结构 ConvNeXt 网络有五种大小&#xff0c;可以参考下面 2、项目实现 完整的项目如下&#xff1a; 这里参考了网上的ConvNeXt 模型&…

Springboot+Vue项目-基于Java+MySQL的个人云盘管理系统(附源码+演示视频+LW)

大家好&#xff01;我是程序猿老A&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;Java毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计 &…

神经网络的基础:从感知机到复杂网络的进化

文章目录 一、神经网络的例子二、理解神经信号的传递2.1 感知机的结构和功能2.2 优化函数2.3 明确表示偏置 三、理解感知机和激活函数的作用3.1 基础感知机模型3.2 激活函数的引入 一、神经网络的例子 理解感知机 在探索神经网络之前&#xff0c;我们首先回顾了感知机的基本概…

iOS抓包工具——Stream

[清风穆云 ](https://cloud.tencent.com/developer/user/4224041) iOS抓包工具——Stream 关注作者 前往小程序&#xff0c;Get_更优_阅读体验&#xff01; 立即前往 腾讯云 开发者社区 文档建议反馈控制台 首页 学习 活动 专区 工具 TVP [最新优惠活动 ](http…

可视化实验三 Matplotlib库绘图及时变数据可视化

1.1 任务一 1.1.1 恢复默认配置 #绘图风格&#xff0c;恢复默认配置 plt.rcParams.update(plt.rcParamsDefault)#恢复默认配置 或者 plt.rcdefaults() 1.1.2 汉字和负号的设置 import matplotlib.pyplot as plt plt.rcParams["font.sans-serif"]"SimH…

Vue3 路由入门

先安装路由 npm i vue-router //创建路由器 import { createRouter, createWebHashHistory } from vue-router//1.导入组件 import Home from /components/Home.vue import News from /components/News.vue//2.配置路由映射规则 const routes [{name: home,path: /home,compo…

学习方法的重要性

原贴&#xff1a;https://www.cnblogs.com/feily/p/13999204.html 原贴&#xff1a;https://36kr.com/p/1236733055209095 1、 “一万小时定律”的正确和误区 正确&#xff1a; 天才和大师的非凡&#xff0c;不是真的天资超人一等&#xff0c;而是付出了持续不断的努力&…

Java数组创建与使用

一.创建和初始化 1.数组是怎么创建的&#xff1f; 直接举例子&#xff1a; int[] arr new int[10]; 这里只简单的举一个int开辟数组的例子。 可见java数组的创建于C语言是不同的。前面是一个int[ ]就是一个数组的数据类型&#xff0c;后面的arr是数组名&#xff0c;最后[…

OpenCV | 项目 | 虚拟绘画

OpenCV | 项目 | 虚拟绘画 捕捉摄像头 如果在虚拟机中运行&#xff0c;请确保虚拟机摄像头打开。 #include<opencv2/opencv.hpp>using namespace cv; using namespace std;int main() {VideoCapture cap(0);Mat img;while(1) {cap.read(img);imshow("Image"…

stm32 st7735驱动 详解

初始化指令 void LCD_Init(void) { #if USE_SIM_SPILCD_SIM_SPI_GPIO_Init(); #endifLCD_RES_0();//复位HAL_Delay(100);LCD_RES_1();HAL_Delay(100);LCD_BLK_1();//打开背光HAL_Delay(100);//************* Start Initial Sequence **********//LCD_SPI_Send_Cmd(0x11); //Sl…

华为eNSP综合实验-网络地址转换

实验完成之后,在AR1的g0/0/1接口抓包,查看地址转换 实现私网pc访问公网pc 实验命令展示 SW1: vlan batch 12 #创建vlan interface e0/0/1 #进入接口配置vlan端口 port link-type access port default vlan 12 q interface e0/0/2 #进入接口配置vlan端口 port link-type ac…

想要买到心仪的旋转式孔板流量计吗?

选择旋转式孔板流量计可不能云里雾里的乱选择呀&#xff0c;煤矿对产品质量要求很严格的。所以我们要先了解产品的再决定才是对的选择。 旋转式孔板流量计技术参数【1--5--9】 规格&#xff1a;DN15&#xff5e;DN1000 孔径比(βd/D)&#xff1a;β0&#xff0e;2—0&#xff…

数组中两个字符串的最小距离

给定一个字符串数组strs&#xff0c;再给定两个字符串str1和str2&#xff0c;返回在strs中str1和str2的最小距离&#xff0c;如果str1或str2为null&#xff0c;或不在strs中&#xff0c;返回-1。 输入描述&#xff1a; 输入包含有多行&#xff0c;第一输入一个整数n(1 ≤ n ≤…