• Redis源码剖析(七)压缩列表


    压缩列表的构成

    压缩列表是为了节省内存而开发的,可以包含任意多个节点(entry),每一个节点可以保存一个字节数组或者一个整数值

    下图展示了压缩列表的各个组成部分,

    • zlbytes:4字节,压缩列表所用的字节数
    • zltail:4字节,记录压缩列表尾节点entryN距离压缩列表的起始地址的字节数。
    • zllen:2字节,压缩列表的节点数量
    • entryX:列表节点
    • zlend:1字节,特殊值 0xFF(十进制255),用于标记压缩列表的末端

    redis没有使用结构体来保存压缩列表的信息,而是通过宏来定位每个成员的地址:

    /*
     * ziplist 属性宏
     */
    // 定位到 ziplist 的 bytes 属性,该属性记录了整个 ziplist 所占用的内存字节数
    #define ZIPLIST_BYTES(zl)       (*((uint32_t*)(zl)))
    
    // 定位到 ziplist 的 offset 属性,该属性记录了到达表尾节点的偏移量
    #define ZIPLIST_TAIL_OFFSET(zl) (*((uint32_t*)((zl)+sizeof(uint32_t))))
    
    // 定位到 ziplist 的 length 属性,该属性记录了 ziplist 包含的节点数量
    #define ZIPLIST_LENGTH(zl)      (*((uint16_t*)((zl)+sizeof(uint32_t)*2)))
    
    // 返回 ziplist 表头的大小
    #define ZIPLIST_HEADER_SIZE     (sizeof(uint32_t)*2+sizeof(uint16_t))
    
    // 返回指向 ziplist 第一个节点(的起始位置)的指针
    #define ZIPLIST_ENTRY_HEAD(zl)  ((zl)+ZIPLIST_HEADER_SIZE)
    
    // 返回指向 ziplist 最后一个节点(的起始位置)的指针
    #define ZIPLIST_ENTRY_TAIL(zl)  ((zl)+intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl)))
    
    // 返回指向 ziplist 末端 ZIP_END (的起始位置)的指针
    #define ZIPLIST_ENTRY_END(zl)   ((zl)+intrev32ifbe(ZIPLIST_BYTES(zl))-1)

    压缩列表节点结构

    prev_entry_len成员

    previous_entry_length 属性的长度可以是 1 字节或者 5 字节

    • 如果前一节点的长度小于 254 字节, previous_entry_length 的长度为 1 字节。
    • 如果前一节点的长度大于等于 254 字节, previous_entry_length 的长度为 5 字节: 其中第一字节会被设置为 0xFE (十进制值 254), 而之后的四个字节则用于保存前一节点的长度。

    下图 展示了一个压缩列表节点,previous_entry_length 为 0x05 , 表示前一节点的长度为 5 字节。

     

    通过 previous_entry_length 属性,我们可以实现从表尾向表头遍历操作:

    举个例子,有一个当前节点起始地址的指针 c , 那么我们只要用指针 c 减去当前节点 previous_entry_length 属性的值, 就可以得出一个指向前一个节点起始地址的指针 p 。

     

    encoding成员

    redis对字节数组和整数编码提供了一组宏定义:

    /*
     * 字符串编码类型
     */
    #define ZIP_STR_06B (0 << 6)
    #define ZIP_STR_14B (1 << 6)
    #define ZIP_STR_32B (2 << 6)
    
    /*
     * 整数编码类型
     */
    #define ZIP_INT_16B (0xc0 | 0<<4)
    #define ZIP_INT_32B (0xc0 | 1<<4)
    #define ZIP_INT_64B (0xc0 | 2<<4)
    #define ZIP_INT_24B (0xc0 | 3<<4)
    #define ZIP_INT_8B 0xfe

    value成员

    value成员负责根据encoding来保存字节数组或整数

    连锁更新

      如果一个压缩列表中,有多个连续、长度介于250字节到253字节之间的节点,因此记录这些节点只需要1个字节的prev_entry_len,如果要插入一个长度大于等于254的新节点到压缩列表的头部,然而原来的节点的prev_entry_len成员长度仅仅为1个字节,无法保存新节点的长度,因此会对新节点之后的所有prev_entry_len成员大小为1字节的节点产生连锁更新。同样的,如果一个压缩列表中,是多个连续的长度大于等于254的节点,当往压缩列表的头部插入一个长度小于254的节点,也会产生连锁更新。另外删除节点也会产生连锁更新。

      下图展示了连锁更新的过程:

  • 相关阅读:
    VCalendar
    xls和xlsx的区别
    alioss的常见问题
    别再写 shǐ 山代码了。。。
    一款高颜值的 MySQL 管理工具,超好用。。
    Spring Boot Admin 横空出世!
    Redis 官方可视化工具,功能真心强大。。。
    Nginx 从安装到高可用,一篇搞定,99% 的人都收藏了。。
    duobule 转 int 问题
    mysql慢sql查询
  • 原文地址:https://www.cnblogs.com/lizhimin123/p/10156204.html
Copyright © 2020-2023  润新知