Redis数据结构(三):双向链表和压缩链表

Redis数据结构系列:

  Redis数据结构(一):对外数据类型和底层数据结构

  Redis数据结构(二):简单动态字符串

  Redis数据结构(三):双向链表和压缩链表

常用的操作命令

  Redis对外的数据结构中,List是一种常用的数据类型,其在底层存储的数据对应的是双向链表压缩链表  在分析这两种链表之前我们先来复习一下Redis中List数据的基本操作。

  •  lpush      

将一个或多个值插入到列表头部。 如果 key 不存在,一个空列表会被创建并执行 LPUSH 操作。 当 key 存在但不是列表类型时,返回一个错误。

LPUSH KEY_NAME VALUE1.. VALUEN
  •  rpush

Redis Rpush 命令用于将一个或多个值插入到列表的尾部(最右边)。

RPUSH KEY_NAME VALUE1..VALUEN
  •  lpop

移出并获取列表的第一个元素;

Lpop KEY_NAME 
  •  lrange

返回列表中指定区间内的元素,区间以偏移量START和END指定, 其中 0 表示列表的第一个元素、1 表示列表的第二个元素、-1 表示列表的最后一个元素、-2 表示列表的倒数第二个元素 ,以此类推;

LRANGE KEY_NAME START END
  •  lrem 

根据参数 COUNT 的值,移除列表中与参数 VALUE 相等的元素

    • count > 0 : 从表头开始向表尾搜索,移除与 VALUE 相等的元素,数量为 COUNT 。
    • count < 0 : 从表尾开始向表头搜索,移除与 VALUE 相等的元素,数量为 COUNT 的绝对值。
    • count = 0 : 移除表中所有与 VALUE 相等的值。
LREM key count VALUE 
  •  lset

通过索引来设置元素的值

LSET KEY_NAME INDEX VALUE 

这里列举的只是最常用的几个真的List的命令,还有一下命令请自行查询,不属于本文章的重点内容;

双向链表 

  如果Redis列表对象保存的所有字符串对象值的长度有超过64字节或者列表对象保存元素数量大于等于512个的时候,就采用linkedlist(双向链表)格式存储

  

  每个节点都是一个listNode,拥有前驱节点,后继节点和值。这就是C语言中的双向链表

typedef struct listNode {
 struct listNode *prev; //前驱节点,如果是list的头结点,则prev指向NULL 
 struct listNode *next;//后继节点,如果是list尾部结点,则next指向NULL 
 void *value; //万能指针,能够存放任何信息 
} listNode;

只要有多个节点就可以组成一个链表了,但是redis再在外面封装了一层,也就是使用adlist.h/list来实现

typedef struct list { 
listNode *head; //链表头结点指针 
listNode *tail; //链表尾结点指针
unsigned long len; //链表长度计数器
 
//下面的三个函数指针就像类中的成员函数一样
void *(*dup)(void *ptr); //复制链表节点保存的值 
void (*free)(void *ptr); //释放链表节点保存的值
int (*match)(void *ptr, void *key); //比较链表节点所保存的节点值和另一个输入的值是否相等 
} list;

  通过代码可以看出reids中的双向链表增加了链表长度计数器的成员变量,同时封装了保存和释放节点的函数;

压缩链表

  ziplist是个经过特殊编码的双向链表,它的设计标是为了提存储效率。ziplist可以用于存储字符串或整数, 其中整数

是按真正的二进制编码的, 并不是编码成字符串序列。 它能以O(1)的时间复杂度在表的两端提供push和pop操作;

  当一个哈希键只包含少量键值对,并且每个键值对的键和值要么就是小整数,要么就是长度比较短的字符串,那么Redis就会使用压缩列表来做哈希键的底层实现,ziplist结构如下:

  • zlbytes:记录压缩链表占据的字节数,包括自身的4个字节,用于内存重分配
  • zltail:尾节点的偏移量,利于实现尾部pop操作
  • zllen:节点数,最多2^16-2,若超过范围则必须转换为多个压缩链表
  • entry:节点
  • zlend:记录压缩链表的尾部,设置为特殊值0xf

  ziplist中的节点结构

<prevlen> <encoding> <entry-data>
    • prevlen:存储上个节点的长度,用以由后往前回到上一个节点
    • encoding:节点的content属性所保存数据的类型以及长度
    • entry-data:节点数据

 参考链接:

redis源码解析(十一)压缩链表ziplist结构分析 

posted @ 2022-05-08 23:29  FOEVERYANG  阅读(487)  评论(0编辑  收藏  举报