Redis底层数据结构-----quickList

本文介绍了Redis 3.2中采用的quicklist数据结构,它是双向链表和压缩链表的结合,用于高效管理List对象。quicklist通过控制节点内的压缩链表来避免频繁更新,提升性能。重点讲解了quicklist的内部结构、添加元素策略和其在内存管理上的优势。

请添加图片描述

quickList介绍

在redis3.0以前,List对象的底层数据结构是双向链表或者压缩链表在3.2的时候,List对象的底层改为由quicklist数据结构实现。
quickList链表就是双向链表+压缩链表组合,quicklist本身是个链表,链表中的元素是压缩列表。

quicklist的数据结构

typedef struct quicklist {
    //quicklist的链表头
    quicklistNode *head;      
    //quicklist的链表尾
    quicklistNode *tail; 
    //所有压缩列表中的总元素个数
    unsigned long count;
    //链表节点的个数 quicklistNode
    unsigned long len;       
    ...
} quicklist;

quicklistNode的结构定义

typedef struct quicklistNode {
    //指向前节点的指针
    struct quicklistNode *prev;     //前一个quicklistNode
    //指向后节点的指针
    struct quicklistNode *next;     //后一个quicklistNode
    //quicklistNode指向的压缩列表
    unsigned char *zl;              
    //压缩列表的的字节大小
    unsigned int sz;                
    //压缩列表的元素个数
    unsigned int count : 16;        //ziplist中的元素个数 
    ....
} quicklistNode;

可以看到 node节点有指向前节点和后节点的指针,以及节点的元素保存的不是单纯的元素值,而是保存了一个压缩链表,所以有个指向压缩链表的指针*zl
数据结构图

请添加图片描述
在向quicklist添加一个元素的时候,不会像普通的链表那样,直接新建一个链表节点。而是会检查插入位置的压缩链表是否能容纳该元素,如果能够容纳,那么就直接保存到quicklistNode结构里面的压缩列表,如果不能容纳,才会新建一个Node结构。
quicklist会控制quicklistNode结构里面的压缩列表的大小或者元素个数,用来规避潜在的连锁更新的风险,但是并没有完全解除。

Redis底层数据结构的设计是其高性能的核心之一。Redis内部通过一系列高效的数据结构来实现对外暴露的数据类型(如string、list、hash、set、zset),这些底层结构包括但不限于sds(简单动态字符串)、dict(字典)、intset(整数集合)、ziplist(压缩列表)、quicklist(快速列表)、listpack和skiplist(跳跃表)等。 ### Redis底层数据结构架构设计 1. **SDS(Simple Dynamic String)** Redis使用SDS来替代传统的C语言字符串,解决了C字符串的一些局限性,例如无法高效地获取字符串长度、容易造成缓冲区溢出等。SDS的结构包含一个长度字段,使得Redis能够以O(1)的时间复杂度获取字符串长度[^1]。 2. **Dict(字典)** 字典是Redis中用于实现哈希表的数据结构,基于数组和链表的组合结构。它支持O(1)时间复杂度的读写操作。Redis中的哈希键(hash key)就是通过字典实现的。字典的底层实现依赖于哈希函数和链表解决冲突,从而保证高效的存储和检索[^3]。 3. **Ziplist(压缩列表)** Ziplist是一种紧凑的、连续内存块的结构,用于存储多个小数据项。它的内存布局包括zlbytes(总字节数)、zltail(最后一项的偏移量)、zllen(数据项数量)、entry(实际数据项)和zlend(结束标记)。每个entry包含前一个节点的长度、当前节点的数据长度和具体的数据内容。这种设计使得ziplist非常适合存储小型数据集合,同时减少了内存碎片[^4]。 4. **Listpack** Listpack是Redis 5.0引入的新结构,用于替代ziplist。与ziplist不同的是,listpack中的每个节点不再记录前一个节点的长度,从而避免了连锁更新的问题。这种改进显著提高了内存使用的效率和性能[^2]。 5. **Quicklist(快速列表)** QuicklistRedislist数据类型的默认实现。它结合了ziplist和双向链表的优点,将多个ziplist节点通过双向链表连接起来,形成一个链表结构。这种设计既保持了内存的紧凑性,又避免了单个ziplist可能带来的性能问题,适合处理大数据量的列表操作。 6. **Intset(整数集合)** Intset是Redis中专门用于存储整数集合的数据结构。它通过动态调整存储方式(int16_t、int32_t或int64_t),尽可能减少内存占用。Intset支持高效的插入、删除和查找操作,适用于存储大量整数且不需要字符串功能的场景。 7. **Skiplist(跳跃表)** SkiplistRedis中zset(有序集合)的底层实现之一。跳跃表通过多层索引结构实现了高效的查找、插入和删除操作,平均时间复杂度为O(log n)。跳跃表的层级设计使得它在处理大规模数据时依然保持较高的性能。 ### Redis底层数据结构的组织关系 Redis底层数据结构之间存在一定的嵌套关系。例如,字典(dict)的实现依赖于哈希表(hashtable),而哈希表的底层实现可能涉及链表(用于处理哈希冲突)。类似地,quicklist的实现结合了ziplist和双向链表的特性,而zset则通过跳跃表(skiplist)和字典(dict)的组合实现了高效的有序集合操作。 ### 示例代码:Redis底层数据结构的模拟实现 以下是一个简化版的ziplist结构模拟实现,展示了如何通过紧凑的内存布局存储数据项: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> typedef struct ziplist { unsigned int zlbytes; // 总字节数 unsigned int zltail; // 最后一个entry的偏移量 unsigned short zllen; // entry数量 char entries[0]; // 数据项 } ziplist; typedef struct entry { unsigned int prevrawlen; // 前一个entry的长度 unsigned int len; // 当前entry的长度 char data[0]; // 数据内容 } entry; // 创建一个新的ziplist ziplist* create_ziplist() { ziplist *zl = (ziplist*)malloc(sizeof(ziplist)); zl->zlbytes = sizeof(ziplist); zl->zltail = 0; zl->zllen = 0; return zl; } // 向ziplist中添加一个entry void add_entry(ziplist *zl, const char *data, unsigned int len) { entry *e = (entry*)malloc(sizeof(entry) + len); e->prevrawlen = zl->zltail; e->len = len; memcpy(e->data, data, len); // 更新ziplist的总字节数和最后一个entry的偏移量 zl->zlbytes += sizeof(entry) + len; zl->zltail = (char*)zl + zl->zlbytes - (char*)zl; zl->zllen++; // 将新entry添加到ziplist的末尾 memcpy(zl->entries + zl->zlbytes - sizeof(ziplist), e, sizeof(entry) + len); free(e); } int main() { ziplist *zl = create_ziplist(); add_entry(zl, "hello", 5); add_entry(zl, "world", 5); printf("Total bytes: %u\n", zl->zlbytes); printf("Number of entries: %u\n", zl->zllen); printf("Last entry offset: %u\n", zl->zltail); free(zl); return 0; } ``` 这段代码模拟了ziplist的基本结构和操作,展示了如何通过紧凑的内存布局存储数据项。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值