一、前言

Redis 提供了5种数据类型:String(字符串)、Hash(哈希)、List(列表)、Set(汇合)、Zset(有序汇合),了解每种数据类型的特点对于redis的开发和运维十分重要。
<p align="right">原文解析</p>

Redis 中的 list 是咱们常常应用到的一种数据类型,依据应用形式的不同,能够利用到很多场景中。

二、操作命令

List数据类型在 Redis 中的相干命令:

命令形容用法
LPUSH1.将一个或多个值value插入到列表key的表头
2.如果有多个value值,那么各个value值按从左到右的程序顺次插入表头
3.key不存在,一个空列表会被创立并执行LPUSH操作
4.key存在但不是列表类型,返回谬误
LPUSH key value [value ...]
LPUSHX1.将值value插入到列表key的表头,当且仅当key存在且为一个列表
2.key不存在时,LPUSHX命令什么都不做
LPUSHX key value
LPOP1.移除并返回列表key的头元素LPOP key
LRANGE1.返回列表key中指定区间内的元素,区间以偏移量start和stop指定
2.start和stop都以0位底
3.可应用正数下标,-1示意列表最初一个元素,-2示意列表倒数第二个元素,以此类推
4.start大于列表最大下标,返回空列表
5.stop大于列表最大下标,stop=列表最大下标
LRANGE key start stop
LREM1.依据count的值,移除列表中与value相等的元素
2.count>0示意从头到尾搜寻,移除与value相等的元素,数量为count
3.count<0示意从从尾到头搜寻,移除与value相等的元素,数量为count
4.count=0示意移除表中所有与value相等的元素
LREM key count value
LSET1.将列表key下标为index的元素值设为value
2.index参数超出范围,或对一个空列表进行LSET时,返回谬误
LSET key index value
LINDEX1.返回列表key中,下标为index的元素LINDEX key index
LINSERT1.将值value插入列表key中,位于pivot后面或者前面
2.pivot不存在于列表key时,不执行任何操作
3.key不存在,不执行任何操作
LINSERT key BEFORE AFTER pivot value
LLEN1.返回列表key的长度
2.key不存在,返回0
LLEN key
LTRIM1.对一个列表进行修剪,让列表只返回指定区间内的元素,不存在指定区间内的都将被移除LTRIM key start stop
RPOP1.移除并返回列表key的尾元素RPOP key
RPOPLPUSH在一个原子工夫内,执行两个动作:
1.将列表source中最初一个元素弹出并返回给客户端
2.将source弹出的元素插入到列表desination,作为destination列表的头元素
RPOPLPUSH source destination
RPUSH1.将一个或多个值value插入到列表key的表尾RPUSH key value [value ...]
RPUSHX1.将value插入到列表key的表尾,当且仅当key存在并且是一个列表
2.key不存在,RPUSHX什么都不做
RPUSHX key value

实际:别偷懒,入手一下,try it out

三、利用场景

1、lpush+lpop=Stack(栈)
2、lpush+rpop=Queue(队列)
3、lpush+ltrim=Capped Collection(无限汇合)
4、lpush+brpop=Message Queue(音讯队列
5、排行榜,数据最新列表等等

四、底层解析

结构图上显示,List类型有两种实现形式:
举例说明,创立列表对象 numbers

1、应用压缩列表(ziplist)实现的列表对象

构造如下

2、应用双端链表(linkedlist)实现的列表对象

构造如下

五、疑难思考

压缩列表与双端链表是什么样的构造?

1、压缩列表(ziplist)

压缩列表(ziplist)是Redis为了节俭内存而开发的,是由一系列非凡编码的间断内存块组成的程序型数据结构,一个压缩列表能够蕴含任意多个节点(entry),每个节点能够保留一个字节数组或者一个整数值。

构造如下

压缩列表的每个节点形成如下

1)previous_entry_ength:以字节为单位,记录了压缩列表中前一个字节的长度。previous_entry_ength 的长度能够是1字节或者5字节:
  如果前一节点的长度小于254字节,那么 previous_entry_ength 属性的长度为1字节,前一节点的长度就保留在这一个字节外面。
  如果前一个节点的长度大于等于254,那么 previous_entry_ength 属性的长度为5字节,其中属性的第一字节会被设置为0xFE(十进制254),而之后的四个字节则用于保留前一节点的长度。
  利用此原理即以后节点地位减去上一个节点的长度即失去上一个节点的起始地位,压缩列表能够从尾部向头部遍历,这么做很无效地缩小了内存的节约。
2)encoding:记录了节点的 content 属性所保留数据的类型以及长度。
3)content:保留节点的值,节点的值能够是一个字节数组或者整数,值的类型和长度由节点的 encoding 属性决定。

2、双端链表(linkedlist)

链表是一种罕用的数据结构,C 语言外部是没有内置这种数据结构的实现,所以Redis本人构建了链表的实现。
链表节点定义:

typedef  struct listNode{       //前置节点       struct listNode *prev;       //后置节点       struct listNode *next;       //节点的值       void *value;  }listNode

多个 listNode 能够通过 prev 和 next 指针组成双端链表,构造如下

另外Redis还提供了操作链表的数据结构:

typedef struct list{     //表头节点     listNode *head;     //表尾节点     listNode *tail;     //链表所蕴含的节点数量     unsigned long len;     //节点值复制函数     void (*free) (void *ptr);     //节点值开释函数     void (*free) (void *ptr);     //节点值比照函数     int (*match) (void *ptr,void *key);}list;

list构造为链表提供了表头指针 head ,表尾指针 tail 以及链表长度计数器 len ,dup、free、match 成员则是用于实现多态链表所需的类型特定函数。

Redis链表实现的个性

  • 双端:链表节点带有 prev 和 next 指针,获取某个节点的前置节点和后置节点复杂度都是O(1)。
  • 无环:表头节点的 prev 指针和表尾节点的 next 指针都指向 NULL,对链表的拜访以NULL为起点。
  • 带表头指针和表尾指针:通过list构造的 head 和 tail 指针,程序获取链表的表头节点和表尾结点的复杂度都是O(1)。
  • 带链表长度计数器:程序应用 list 构造的 len属性对 list持有的链表节点进行计数,程序获取链表中节点数量的复杂度为O(1)。
  • 多态:链表节点应用 void* 指针来保留节点值,并且通过 list 构造的 dup、 free、match 三个属性为节点值设置类型特定函数,所以链表能够用于保留各种不同类型的值。

疑难:Redis列表什么时候会应用 ziplist 编码,什么时候又会应用 linkedlist 编码呢?下节再说...