Redis List底层实现:链表

初识链表
常用指令
  1. RPUSH

    格式:RPUSH 链表键名 一个或多个value

    说明:Right Push,依次将zhang3、li4、wang5从右边入队,结果是zhang3靠左,wang5靠右(尾插法,支持批量操作)。

    返回:成功插入后列表的元素数量

    时间复杂度:O(1)

localhost:1>RPUSH users zhang3 li4 wang5
"3"
  1. LRANGE
    格式:LRANGE 链表键名 开始下标 结束下标

    说明:List Range,从左数起,获取列表下标范围是0到2的元素,当结束下标为-1时,代表最后一个元素的下标

    返回:指定范围内的元素,从上往下依次对应列表中从左到右的元素

    时间复杂度:O(n),慎用

localhost:1>LRANGE users 0 2
 1)  "zhang3"
 2)  "li4"
 3)  "wang5"
 
 # 结束下标为-1,遍历到最后一个元素
localhost:1>LRANGE users 0 -1
 1)  "zhang3"
 2)  "li4"
 3)  "wang5"
  1. LPUSH

    格式:LPUSH 链表键名 一个或多个value

    说明:Left Push将元素li2从左边入队(头插法,支持批量)

    返回:成功插入后列表的元素数量

    时间复杂度:O(1)

localhost:1>LPUSH users li2
"4"
localhost:1>LRANGE users 0 3
 1)  "li2"
 2)  "zhang3"
 3)  "li4"
 4)  "wang5"
  1. LPOP

    格式:LPOP 链表键名

    说明:将最左边的元素出队,即li2,列表长度减1

    返回:本次指令操作被出队的元素(li2)

    时间复杂度:O(1)

localhost:1>LPOP users
"li2"
localhost:1>LRANGE users 0 -1
 1)  "zhang3"
 2)  "li4"
 3)  "wang5"
  1. RPOP

    格式:RPOP 链表键名

    说明:将最左边的元素出队,即wang5,列表长度减1

    返回:本次指令操作被出队的元素(wang5)

    时间复杂度:O(1)

localhost:1>RPOP users
"wang5"
localhost:1>LRANGE users 0 -1
 1)  "zhang3"
 2)  "li4"

其他命令
  1. LINDEX

    格式:LINDEX 链表键名 元素下标

    说明:获取链表中指定下标的元素值

    返回:对应下标的元素

    时间复杂度:O(n),需要从头开始遍历链表,慎用

localhost:1>lindex users 0
"zhang3"
localhost:1>lindex users 1
"li4"
localhost:1>lindex users 2
null
localhost:1>lindex users -1
"li4"
  1. LLEN

    格式:LLEN 链表键名

    说明:获取链表长度

    返回:链表长度(节点数量)

    时间复杂度:O(1),直接读取结构体的len属性

localhost:1>llen users
"2"
  1. LTRIM

    格式:LTRIM 开始下标 结束下标

    说明:去掉指定下标范围外的元素,与平时字面意思不同,更应该理解成LRETAIN(保留)。当结束下标-开始下标为负数时,相当于清空链表,慎用。

    返回:OK则成功

    时间复杂度:O(n)

# 把下标为0的zhang3移除
localhost:1>ltrim users 1 1
"OK"
localhost:1>lrange users 0 -1
 1)  "li4"

结构体
链表节点listNode
结构体
typedef struct listNode {
    //前驱节点,指向前一个节点
    struct listNode* prev;
    //后继结点,指向后一个节点
    struct listNodee* next;
    //节点值,当前节点的值,因为是void*类型,所以可以存储各种类型的值
    void* value;
} listNode;
图解链表节点关系

一条链表是由一个或多个链表节点连接而成的

Redis链表节点图解


链表list

​ 为了更方便地管理链表,使用一个list结构来持有链表中常用的链表信息

结构体
typedef struct list {
    //头结点,指向链表第一个节点,其prev为NULL
    listNode* head;
    //尾节点,指向链表最后一个节点,其next为NULL
    listNode* tail;
    //链表长度,即当前链表节点数量
    int len;
    //节点值复制函数duplicated
    void *(*dup)(void *ptr);
    //节点值释放函数
    void (*free)(void *ptr);
    //节点值对比函数
    int (*match)(void *ptr, void *key);
}
图解链表数据结构

Redis链表list图解

特点
  1. 双端链表:链表有prev和next分别指向上一节点和下一节点,构成了双端链表;
  2. 无环:Redis中d链的链表是无环的,即头节点的prev不会指向尾节点,尾节点的next不会指向头结点,遍历到NULL时为终点;
  3. 带头节点和尾节点:访问头、尾节点的时间复杂度为O(1);
  4. 长度计数器:获取长度的复杂度为O(1),而不需要以O(N)复杂度从头到尾遍历链表;
  5. 多态:提供了dup、free、match三个函数,可以操作不同类型的节点值。

总结
  1. 链表被广泛用于实现Redis各种功能,如列表键、发布与订阅、慢查询、监视器等;
  2. 链表节点由一个listNode结构来表示,每个节点都有指向前一个节点的前驱节点和指向后一个节点的后继结点,构成了双端链表;
  3. 每个链表都由一个list结构来表示,有表头节点指针,表尾节点指针和链表长度等信息,可以让Redis在部分情况下进行O(1)复杂度的操作;
  4. 因为list的表头节点head的prev和表尾节点tail的next都指向NULL,所以Redis的链表是一条无环链表;
  5. 通过为链表设置不同的类型特定函数dup、free和match,Redis的链表可以保存各种不同类型的数据值;

参考
  1. 《Redis设计与实现》 – 黄健宏

  2. 《Redis深度历险:核心原理和应用实践》-- 钱文品

  3. 源码

    (1) 链接: https://github.com/huangz1990/redis-3.0-annotated/blob/unstable/src/t_list.c

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值