zoukankan      html  css  js  c++  java
  • redis 笔记01 简单动态字符串、链表、字典、跳跃表、整数集合、压缩列表

    文中内容摘自《redis设计与实现》

    简单动态字符串

    1. Redis只会使用C字符串作为字面量,在大多数情况下,Redis使用SDS(Simple Dynamic String,简单动态字符串)作为字符串表示。

    2. SDS包含以下三个属性:

        1). free : 记录buf数组中未使用字节的数量

        2). len : 记录buf数组中已使用字节的数量,等于SDS所保存字符串的长度

        3). buf : char类型数组,用于保存字符串,最后一个字节是一个空字符''

    3. SDS遵循C字符串以空字符串结尾的惯例,保存空字符的1字节空间不计算在SDS的len属性里面。遵循空字符串惯例的好处是,SDS可以直接重用一部分C字符串函数库里的函数。

    4. 比起C字符串,SDS具有以下优点:

        1). 常数复杂度获取字符串长度,通过len属性直接获取。C字符串需要遍历所有字符串内容。

        2). 杜绝缓冲区内存溢出。SDS会在插入新数据前判断是否需要扩充内存。C字符串需要手动扩充。

        3). 减少修改字符串长度时所需的内存重分配次数。主要有两种方式: 空间预分配 和 惰性空间释放

        4). 二进制安全。SDS可以保存文本或者二进制数据,而C字符串只能保存文本数据。

        5). 兼容部分C字符串函数。

    5. 二进制安全:虽然数据库一般用于保存文本数据,但使用数据库来保存二进制数据的场景也不少见。为了确保Redis可以适用于各种不同的使用场景,SDS的API都是二进制安全的,

                         所有SDS API都会以二进制的方式来处理SDS存放在buf数组里的数据。程序不会对其中的数据做任何的限制、过滤、或者假设,数据写入时什么样的,他被读取就是什么样的。

    链表:

    1. 链表提供了高效的节点重排能力,以及顺序性的节点访问方式,并且可以通过增删节点来灵活地调整链表的长度。

    2. 链表节点主要含有三个属性:

       1). * prev : 前置节点

       2). * next : 后置节点

       3). * value : 节点的值

    3. 链表主要包含以下几个属性:

        1). * head : 表头节点

        2). * tail : 表尾节点

        3). len :链表包含的节点数量

        4). dup函数:用于复制链表节点所保存的值。

        5). free函数:用于释放节点所保存的值

        6). match函数:用于对比链表节点所保存的值和另一个输入值是否相等

    4. Redis的链表实现的特征可以总结如下:

        1). 双端:链表节点带有prev和next指针,获取某个节点的前置节点和后置节点的复杂度都是O(1)

        2). 无环:表头节点的prev指针和表尾节点的next指针都指向NULL,对链表的访问以NULL为节点

        3). 带表头指针和表尾指针:通过list结构的head指针和tail指针,程序获取链表的表头节点和表尾节点的复杂度为O(1)

        4). 带链表长度计数器:程序使用list结构的len属性来对list持有的链表节点进行计数,程序获取链表中节点数量复杂度为O(1)

        5). 多态:链表节点使用void*指针来保存节点值,并且可以通过list结构的dup、free、match三个属性为节点值设置类型特定函数,所以链表可以用于保存各种不同类型的值。

    5. 链表被广泛用于redis的各种功能,比如列表键、发布与订阅、慢查询、监视器等

    字典:

    1. 字典,又称为符号表,关联数组或映射,是一种用于保存键值对的抽象数据结构。

    2. Redis的字典使用哈希表作为底层实现,一个哈希表里面可以有多个哈希表节点,而每个哈希表节点就保存了字典中的一个键值对。

    3. 哈希表主要包含以下几个属性:

        1). table : 哈希表数组

        2). size : 哈希表大小

        3). sizemask : 哈希表大小掩码,用于计算索引值;总是等于size - 1

        4). used : 该哈希表已有节点的数量

    4. 哈希表节点主要包含以下几个属性:

        1). key : 键

        2). v : 值。其中键值对的值可以是一个指针,或者是一个unit64_t的整数,又或者是一个int64_t整数

        3). next : 指向另一个哈希表节点的指针,这个指针可以将多个哈希值相同的键值对连接在一起,以此来解决键冲突的问题。

    5. 字典主要包含以下几个属性:

        1). type : 一个指向dictType结构的指针,每个dictType结构保存了一簇用于操作特定类型键值对的函数,Redis会为用途不同的字典设置不同的类型特定函数。

        2). privdata : 保存了需要传给哪些类型特定函数的可选参数。

        3). ht : 包含两个项的数组,数组中的每个项都是一个dictht哈希表,一般情况只是用ht[0]哈希表,ht[1]只会在对ht[0]进行rehash时使用。

        4). trehashidx : 记录rehash目前的进度。如果目前没有进行rehash,那么它的值为-1.

    6. 字典被广泛用于实现Redis的各种功能,其中包括数据库和哈希键。

    7. Redis中的字典使用暗黑系表作为底层实现,每个字典带有两个哈希表,一个平时使用,另一个仅在进行rehash时使用。

    8. 哈希表使用链地址法来解决键冲突,被分配到同一个索引上的多个键值对会连接成单向链表。最新的键值在最上面,便于访问。

    9. 在对哈希表进行扩展或者收缩操作时,程序需要将现有的哈希表包含的所有键值对rehash到新哈希表里面,并且这个rehash过程并不是一次性完成的,而是渐进式的。

    10. 因为在进行渐进式rehash的过程中,字典会同时使用ht[0]和ht[1]两个哈希表,所有在渐进式rehash进行期间,字典的删除、查找、更新等操作会在两个哈希表上进行。先找ht[0],再找ht[1].

          另外,在渐进式rehash期间,新添加到字典中的键值对一律被保存到ht[1]中。

    11. 哈希表的扩展与收缩:

          满足以下任意条件时,执行扩展操作:

          1). 服务器目前没有在执行BGSAVE或者BGREWRITEAOF命令,并且哈希表的负载因子大于等于1。

          2). 服务器目前正在执行BGSAVE或者BGREWRITEAOF命令,并且哈希表的负载因子大于等于5

          另一方面,当哈希表的负载因子小于0.1时,程序自动开始对哈希表执行收缩操作。

    跳跃表:

    1. 跳跃表是以各种有序数据结构,它通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的。

    2. Redis只有两个地方用到跳跃表:一个是实现有序集合键,另一个是在集群节点中用作内部数据结构。

    3. Redis的跳跃表zskiplistNode和zskiplist两个结构定义,其中zskiplistNode结构用于表示跳跃表节点,而zskiplist结构则用于保存跳跃表节点的相关信息,比如:节点的数量以及指向表头节点和表尾节点的指针等。

    4. zskiplistz主要包含一下几个属性:

        1). header : 指向跳跃表的表头节点

        2). tail : 指向跳跃表的表尾节点

        3). level : 记录目前跳跃层内,层数最大的那个节点的层数(表头节点的层数不计算在内容) 

        4). length : 记录跳跃表的长度,也即是,跳跃表目前包含节点的数量(表头节点不计算在内,表头节点好像是最高层)

    5. zskiplistNode主要包含以下几个属性:

        1). zskiplistLevel : 层,里面包含前进指针(forward) 和跨度(span)

        2). backward : 后退指针

        3). score : 分值

        4). obj : 成员对象

    6. 跳跃表是有序集合的底层实现之一。

    7. 每个跳跃表节点的层高都是1至32之间的随机数。

    8. 在同一个跳跃表中,多个节点可以包含相同的分值,但每个节点的成员对象必须是唯一的。

    9. 跳跃表的节点按照分值大小进行排序,当分值相同时,节点按照成员对象的大小进行排序。

    整数集合

    1. 整数集合是集合键的底层实现之一,当一个集合只包含整数值元素,并且这个集合的元素数量不多时,Redis就会使用整数集合作为集合键的底层实现。

        整数集合可以保存的类型为:int16_t,int32_t,int64_t 的整数值,并且保证集合中不会出现重复元素。

    2. 整数集合主要包含以下几个属性:

        1). encoding : 编码方式。值可以为:int16_t,int32_t,int64_t 

        2). length : 集合包含的元素数量

        3). contents : 保存元素的集合。数组中元素按从小到大的顺序排列。contents数组的真正类型取决于encoding属性的值。

    3. 当我们要将一个新元素添加到整数集合里面,并且新元素的类型比整数集合现有所有元素的类型都要长时,整数集合需要先进行升级,然后才能将新元素添加到整数集合里面。

    4. 整数集合的升级策略有两个好处:一个是提升整数集合的灵活性(可以随意添加int16_t,int_32,int_64类型的整数到集合,而不必担心类型错误),另一个是尽可能地节约内存。

    5. 整数集合只支持升级操作,不支持降级操作。

    压缩列表:

    1. 当一个列表键只包含少量列表项,并且每个列表项要么就是小整数值,要么就是长度比较短的字符串,那么Redis就会使用压缩列表来做列表键的底层实现。

    2. 压缩列表是Reids为解决内存而开发的,是由一系列特殊编码的连续内存块组成的顺序型数据结构。一个压缩列表可以包含任意多个节点(entry),每个节点可以保存一个字节数组或者一个整数值。

    3. 压缩列表主要包括以下几个属性:

        1). zlbytes : 记录整个压缩列表占用的内存字节数。

        2). zltail : 记录压缩列表表尾节点距离压缩列表的起始地址有多少节点:通过这个偏移量,程序无须遍历整个压缩列表就可以确定表尾节点的地址。

        3). zllen : 2个字节长度。记录了压缩列表包含的节点数量:当这个节点的值小于UINT16_MAX(65535)时,这个属性的值就是压缩列表包含节点的数量;当这个值等于UINT16_MAX时,节点的

                       真实数量需要遍历正而过压缩列表才能计算得出。

        4). entyX : 压缩列表包含的各个节点,节点的长度由节点保存的内容决定。

        5). zlend : 特殊值0xFF(十进制255),用于标记压缩列表的末端。

    4. 指向尾节点的指针可以通过指向压缩列表起始地址的指针加上zltail属性的值得出。

    5. 压缩列表节点包含以下几个属性:

        1). previous_entry_length : 记录压缩列表前一个节点的长度。长度值可以是1字节或者5字节。程序可以通过指针运算,根据当期节点的起始地址来计算出前一个节点的起始地址。

        2). encoding : 记录了节点content属性所保存数据的类型以及长度。

        3). content : content 属性负责保存节点的值,节点值可以是一个字节数组或者整数,值的类型和长度由节点encoding属性决定。

    6. 添加新节点到压缩列表中或者从压缩列表中删除节点,可能会引发连锁更新操作,但这种操作出现的几率并不高。

    7. 压缩列表是一种为节约内存而开发的顺序型数据结构。

  • 相关阅读:
    解决“ 故障模块名称: clr.dll ”
    关于阿里云专有网络搭建FTP服务器的深坑
    电脑异常断电,IDEA崩溃
    Winform 出现“Win已停止工作”解决方法
    C# WinForm控件、自定义控件整理(大全)
    cmd获取管理员权限等
    检测笔记本电池状态
    单片机
    常用工具、焊接技术
    元器件
  • 原文地址:https://www.cnblogs.com/Jtianlin/p/5097568.html
Copyright © 2011-2022 走看看