Redis数据结构

Redis数据结构

一、string

三种编码方式:int、embstr、raw

  • EMBSTR:数据连续存储
  • RAW: 数据链式存储

SDS

自定义数据结构,区别于C语言中的char[]。

优点

  1. 记录长度,时间复杂度为O(1)
  2. 二进制安全
  3. 增加空余空间(alloc-len),为后续追加数据留余地;

alloc规则:min(2len,len+1M)

二、List

6之前最大元素个数:2^32-1

6及6之后:2^64-1

底层:ZIPLIST、linkedlist

  • ziplist:list中的字符串紧凑存在一起

  • linkedlist:list中的字符串对象以链表形式连接

2.1 quicklist

ziplist+linkedlist:由多个ziplist使用链式存储的方式连接

redis7之后ziplist被listpack取代(连锁更新问题)

c 复制代码
// from Redis 5.0.5
typedef struct list {
    listNode *head;
    listNode *tail;
    void *(*dup)(void *ptr);
    void (*free)(void *ptr);
    int (*match)(void *ptr, void *key);
    unsigned long len; // 记录了表的长度
} list;

2.2 压缩列表

zlbytes:表示整个压缩列表所占的空间

zltail:相对于起始指针的偏移量,也就是可以定位到最后一个节点

zllen:表示有多少个数据节点

zlend:表示ziplist的结束

entry结构

结构: <prevlen><encoding><entry-data>

  • prevlen: 表示上个节点的数据长度,小于254,则使用一个字节,大于等于254则使用5字节,这5个字节中,第一个字节是11111110(254),后4个字节表示真实的长度

​ 为什么是5字节而不是2字节:是由于redis设计是这样的,意思是要么就表示长度小的,要么就表示长度大的,中间长度的就使用大的来表示。

  • encoding:编码类型。包含了entry的长度信息
  • entry-data:实际的数据

操作复杂度:

  • 查:

    • 查总数:zllen中有记录(但是如果数据超过65535,zllen会变成0,这个时候需要遍历)
    • 查数据:遍历
  • 增删:

    增加节点的长度之后,后面的节点的prevlen需要更新,可能需要由1字节更新为5字节,后面的后面的节点也需要更新,所以需要进行多次更新。(连锁更新)

删除元素之后,prevlist不会由5字节变成1字节

listpack

结构: <encoding-type><element-data><element-tot-len>

  • encoding-type:编码

  • element-data:数据

  • element-tot-len:整个节点除去element-tot-len的长度,每个字节的第一位0表示停止,1表示继续

例如:

element-tot-len: 0000 0001 1000 0100

下一个元素会从上个元素的element-tot-len的最后一位开始遍历,直到遍历到第一个字节为0位置,

上述例子遍历到 000 0001 000 0100 => 132 表示遍历结束位置再往前132字节就可以找到上一个节点的起始位置

三、Set

编码方式:

  1. intset:查询时使用二分查找,有序,只能存整数,如果存入非整数编码会变成hashtable

  2. hashtable

四、hash

编码格式

  • 压缩列表
  • hashtable

hashtable底层

和java中hashmap差不多

table:可以看成一个数组,每个数组都是存储entry的结构

size:表示table的大小

used:表示已有节点数量

hash冲突使用拉链法(同hashmap,使用头插法)

  • 渐进式扩容:

    rehashidx标志位 -1 -> 0 表示开始扩容 (rehashidex是一个指针,从头开始遍历,迁移元素),和concurrenthashmap类似。

  • 负载因子:

    负载因子 >= 1 且没有在执行写RDB或AOF (bgsave 或 bgrewriteraof)

    负载因子 >= 5 强制扩容

    负载因子 < 0.1 缩容 也是使用渐进式
    java中hashmap和arraylist不支持缩容

五、Zset

编码:

ziplist/skiplist+hashtable

  • zpilist

  • 跳表+hashtable

    查询节点总数复杂度:

    ziplist和跳表都是O(1)

为什么zset使用跳表外还同时使用hashtable

当ZSet要根据成员来查找分值的时候只用使用字典来查找,时间复杂度为O(1)。而当ZSet要执行范围操作时,比如ZRANK、ZRANGE等命令时,将使用原本就有序的跳跃表来实现。

跳表底层

是否能增加一层的概率为25%,最大层数5.0是64,7.0是32

操作复杂度:O(logn)

注:上图是逻辑上的结构

实际结构没有这么多的节点,而是在每个节点中维护一个数组,记录每个节点的后继节点,对应于图中下方部分的结构。

跳表定义的源码:

c 复制代码
typedef struct zskiplist {
    struct zskiplistNode *header, *tail;
    // 节点数量
    unsigned long length;
    int level;
} zskiplist;
c 复制代码
// Redis 7.0.8​
/* ZSETs use a specialized version of Skiplists */​
typedef struct zskiplistNode {
    sds ele;
    double score;
    struct zskiplistNode *backward;
    struct zskiplistLevel {
        struct zskiplistNode *forward;
        unsigned long span;
    } level[];
} zskiplistNode;

六、 对象删除

设置过期时间:set k v ex 过期时间

删除:

  1. 定时删除
  2. 惰性删除
  3. 定期删除
    使用:定期删除+惰性删除
相关推荐
riri191927 分钟前
算法分析:蛮力法
数据结构·算法
摄殓永恒31 分钟前
猫咪几岁
数据结构·c++·算法
似水এ᭄往昔1 小时前
【数据结构】——队列
c语言·数据结构·c++·链表
水水沝淼㵘2 小时前
嵌入式开发学习日志(数据结构--双链表)Day21
c语言·数据结构·学习·算法·排序算法
ElseWhereR2 小时前
反转链表 - 简单
数据结构·链表
HumoChen993 小时前
jedis+redis pipeline诡异的链接损坏、数据读取异常问题解决
redis·pipeline·jedis
共享家95273 小时前
红黑树解析
数据结构·c++·算法
CircleMouse3 小时前
springboot如何通过提供的注解方式来操作Redis
java·spring boot·redis·spring·mybatis
夜晚中的人海4 小时前
【C语言】初阶数据结构相关习题(二)
c语言·开发语言·数据结构