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

简单动态字符串

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

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

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

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

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

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. 压缩列表是一种为节约内存而开发的顺序型数据结构。

redis 笔记01 简单动态字符串、链表、字典、跳跃表、整数集合、压缩列表的更多相关文章

  1. redis底层数据结构--简单动态字符串 链表 字典 跳跃表 整数集合 压缩列表

    1.动态字符串 redis中使用c语言的字符床存储字面量,默认字符串存储采用自己构建的简单动态字符串SDS(symple dynamic string) redis包含字符串的键值对都是用SDS实现的 ...

  2. 【redis】redis底层数据结构原理--简单动态字符串 链表 字典 跳跃表 整数集合 压缩列表等

    redis有五种数据类型string.list.hash.set.zset(字符串.哈希.列表.集合.有序集合)并且自实现了简单动态字符串.双端链表.字典.压缩列表.整数集合.跳跃表等数据结构.red ...

  3. [Redis]Redis的设计与实现-链表/字典/跳跃表

    redis的设计与实现:1.假如有一个用户关系模块,要实现一个共同关注功能,计算出两个用户关注了哪些相同的用户,本质上是计算两个用户关注集合的交集,如果使用关系数据库,需要对两个数据表执行join操作 ...

  4. Redis源码解析:01简单动态字符串SDS

    Redis没有直接使用C字符串(以'\0'结尾的字符数组),而是构建了一种名为简单动态字符串( simple  dynamic  string, SDS)的抽象类型,并将SDS用作Redis的默认字符 ...

  5. Redis数据结构之简单动态字符串SDS

    Redis的底层数据结构非常多,其中包括SDS.ZipList.SkipList.LinkedList.HashTable.Intset等.如果你对Redis的理解还只停留在get.set的水平的话, ...

  6. Redis中的简单动态字符串

    Redis没有直接使用C语言传统的字符串表示(以空字符结尾的字符数组,以下简称C字符串),而是自己构建了一种名为简单动态字符串(simple dynamic string,SDS)的抽象类型,并将SD ...

  7. Redis数据结构之简单动态字符串

    Redis没有直接使用C语言传统的字符串表示(以空字符结尾的字符数组), 而是自己构建了一种名为简单动态字符串(simple dynamic string,SDS)的抽象类型, 并将SDS用作Redi ...

  8. Redis核心原理-简单动态字符串SDS

    SDS简介 Redis是C语言编写的,但没有使用c语言的字符串结构,而是自己实现了一套简单动态字符串 simple dynamic string 简称SDS,SDS兼容C语言的字符串类型,原理类似Ja ...

  9. redis 学习笔记二 (简单动态字符串)

    redis的基本数据结构是动态数组 一.c语言动态数组 先看下一般的动态数组结构 struct MyData { int nLen; char data[0]; }; 这是个广泛使用的常见技巧,常用来 ...

随机推荐

  1. Androidz之字符串国际化问题

    (1)字符串的国际化 在res下写两个values 注意:命名方式都是固定的 一个中文版:values-zh 一个英文版:values-en 可是<string name  要写成一样的,这样会 ...

  2. 边缘检测算子和小波变换提取图像边缘【matlab】

    Roberts边缘检测算子:根据一对互相垂直方向上的差分可用来计算梯度的原理,采用对角线方向相邻两像素之差. 小波变换的方法比较适用于展现夹带在正常信号中的瞬间反常现象,具有方向敏感性.所以可以边缘检 ...

  3. C# 各版本的新特性

    http://www.cnblogs.com/JeffreySun/archive/2012/11/14/2770211.html

  4. IntelliJ IDEA2017 java连接mysql数据库并查询数据

    最近自己开始重新学习java基础了,做java开发不可避免要处理数据库,由于好久不写java了,对idea也有点陌生了.所以这里写篇用jdbc来连接mysql的文章 至于mysql怎么装,请自行百度  ...

  5. SurvivalShooter学习笔记(七.玩家射击)

    玩家射击:(这个脚本放在玩家的空子物体上,这个位置为枪口位置) 点击鼠标,玩家射击: 射击枪口发光,射击通过射线,方向有激光效果:(关于射线不明白可以参考Unity射线相关) 射击有射击音效 射击有每 ...

  6. hdu1066(经典题)

    求N个数阶乘末尾除0后的数值. 主要的难点在于要把这个N个数所含的2和5的队数去掉. 网上方法很多很好. 不多说 Last non-zero Digit in N! Time Limit: 2000/ ...

  7. 《从零开始学Swift》学习笔记(Day60)——Core Foundation框架

    原创文章,欢迎转载.转载请注明:关东升的博客 Core Foundation框架是苹果公司提供一套概念来源于Foundation框架,编程接口面向C语言风格的API.虽然在Swift中调用这种C语言风 ...

  8. chardet库:识别文件的编码格式

    chardet库文档 http://chardet.readthedocs.io/en/latest/usage.html 小文件的编码判断 detect函数只需要一个 非unicode字符串参数,返 ...

  9. finereport Web工具栏

    1.获取报表工具栏按钮 在报表页面中,偶尔会有需要获取工具栏按钮的需求,之前只能通过contentPane.toolbar.options.items[index]来获取,比较不方便,现在我们提供了两 ...

  10. iOS之block,一点小心得

    作为一个iOS开发程序员,没用过block是不可能的.这次我探讨的是block原理,但是有些更深层次的东西,我也不是很清楚,以后随着更加了解block将会慢慢完善. 第一个问题,什么是block? 我 ...