题记:这本书是2015年11月份开始读的,大约花了一个多月的时间通读了一遍,最近由于需要对redis做一些深入的了解,因此又花了两个多月仔细精读了一遍,由于本书设计的内容较多,且每部分的内容都比较细致,因此在整理读书笔记的时候花了很多时间,但确实也收获了很多,本书是针对redis底层的数据结构部分做的整理。 -----Dimmacro 2016年11月7日18:21:54。

我们知道redis可以存储字符串、列表、哈希对象、集合、有序集合等五种对象类型,但是在redis内部,根据不同对象类型的数据特点,有对应的数据结构作为其底层的存储结构,而且即使是同一对象类型,当数据量或数据大小不同时,为了提供更好的性能及减少内存使用率,也会切换不同的底层数据结构。因此了解这些底层数据结构对我们深入了解redis分布式缓存有非常重要的帮助。

(一)数据结构部分-字符串
  1. redis是键值对数据库,每个键都是字符串对象,而值可以是字符串对象,列表对象,哈希对象,集合对象,有序集合对象五种之一;
  2. redis使用sds(simple dynamic string)简单动态字符串来表示最基本的字符串数据,该结构记录了用于保存字符串的字节数组char buf[]、已使用长度int len和未使用长度int free。有点类似于java中的String对象。
  3. 此sds利用c字符串作为字面量,并遵循以空字符'\0'作为字符串末尾的C风格,使得其可以直接重用C字符串函数库的部分函数,但相比较于C字符串有以下优点:
    • 直接保存字符串长度而不是像C那样需要遍历才能获取长度;
    • 通过空间预分配及惰性空间释放来减少由于修改字符串带来的内存重分配。空间预分配是指:当需要扩展字符数组容量时,如果分配后的长度将小于1MB,那么会预分配与当前len长度一样的字节量,如果超过1MB,则会分配1MB。惰性空间释放是指:当缩短sds字符串时,多余出来的字节数组并不回收,而是通过增长free记录起来,这样下次当需要增长到时候如果free本身就够用了,就不需要申请内存了。当然,也有API可调用来主动释放。
    • 使用二进制方式处理buf数组,保持二进制数据,因此可以保存除文本数据外的其他格式,如图片,音视频,压缩文件等;
 

(二)数据结构部分-链表linkedlist

  1. 链表通过高效的节点重排、顺序访问、增删节点灵活调整期长度等特点,应用于redis中的列表键、发布与订阅、慢查询、监视器等;
  2. 链表的数据结构:表头head、表尾tail、节点数量即长度len、节点值复制函数dup、释放函数free、节点值对比函数match;
  3. 表中节点数据结构:前置节点prev、后置节点next、节点值value;
  4. 链表特点:双端、无环、表头表尾指针、长度计数器、多态(使用void *指针来保存节点值,可以用于保存各种不同类型的值)
 
(三)数据结构部分-字典hashtable(字典--->哈希表2张--->哈希表数组--->哈希表节点
  1. 字典是redis数据库的底层实现,对数据的增删查改操作都是构建在字典操作上的;
  2. 字典dict数据结构:类型特定函数dictType(多态字典)、私有函数void *privdata(类型特定的参数)、哈希表数据dictht ht[2](两个哈希表用于做rehash和渐进式hast);
  3. 哈希表是字典的底层实现,其结构为:dictEntry **table哈希表数组、long size哈希表大小、long sizemask哈希表掩码,用于计算索引值、long used哈希表已有节点数量、int trehashidx rehash是否在进行的标识;
  4. 哈希表数组的元素是哈希表节点,是保存字典中键值对的地方,即真正保存数据的地方,其结构为:*key键指针、v值、dictEntry *next下个哈希表节点,用于哈希值相同时,将当期值插入到表头,形成列表,解决键冲突问题;
  5. 哈希过程:根据键值使用MurmurHash2算法计算哈希值,然后与表掩码取模,得到index作为存放哈希表数组的位置,如果当前index已经有节点了,则在此节点头插入当前节点,形成链表。类似java中map结构put值的过程;
  6. rehash的过程:当字典中的哈希表[0]变化的时候,为了让负载因子维持在合理范围,会做rehash操作,其步骤如下:
    • 为哈希表[1]分配空间,并根据扩展还是压缩操作设置其大小,值为2的N次方,扩展值为第一个大于等于ht[0]*used*2的2的n次幂,收缩操作为第一个大于等于ht[0]*used的2的N次幂;
    • 将ht[0]中的所有键值rehash到ht[1]上,完成后释放ht[0],并间ht[1]设为ht[0],并重新创建一个空白的ht[1哈希表为下一次rehash做准备;(如果键值量大,会采用渐进式rehash的方式,在此期间会同时使用ht[0]和ht[1])。
(四)数据结构部分-跳跃表skiplist
  1. 跳跃表是一种有序数据结构,通过在每个节点中维持多个指向其他及节点的指针达到快速访问节点的目的;
  2. redis使用跳跃表用作实现有序集合键以及在集群节点中用作内部数据结构
(五)数据结构部分-整数集合intset
  1. 用于少量整数的集合,是集合键的底层实现之一;
  2. 整数集合intset由encoding编码方式、length包含元素数量、contents元素数组三部分组成,各项在数组contents中按值的大小从小到大有序排列,不包含重复项;
  3. 当新添加的元素值大于现有集合encoding制定的范围时引发升级,现有元素所在位数增加;升级操作可以提升存储数据的灵活性,并节约内存;
  4. 不支持降级操作
(六)数据结构部分-压缩列表ziplist
    1. 压缩列表是一系列特殊编码的连续内存块组成的顺序型数据结构,为节约内存而开发,是列表键和哈希键的底层实现之一;
    2. 组成部分:zlbytes:4个字节,记录整个压缩列表占用的内存字节数;zltail4个字节,记录表尾节点距离压缩列表起始地址有多少字节;zllen,2个字节,记录压缩列表包含的节点数量;entryX列表节点;zlend:1个字节,特殊值0xFF用于标记压缩列表的末端;
    3. 列表节点由三部分构成:previous_entry_length记录压缩列表中前一个节点的长度,可用于从表尾向表头遍历;encoding记录本节点所保存的数据类型及长度;content保存节点值,可以是字节数组或整数;
    4. 当前一节点长度小于254字节,previous_entry_length占1字节,否则占5字节,因此有可能插入一个长度大于254字节的节点到表头是,如果后续节点都介于250到253之间,则可能发生连锁更新,影响性能。不过概率很小。

######以上文字来自Dimmacro,转载请说明来源:http://www.cnblogs.com/dimmacro/ #######

Redis设计与实现-内部数据结构篇的更多相关文章

  1. 共读《redis设计与实现》-数据结构篇

    准备将之前攒下的书先看一遍,主要是有个大概的了解,以后用的时候也知道在哪里找.所以准备开几篇共读的帖子,激励自己多看一些书. Redis 基于 简单动态字符串(SDS).双端链表.字典.压缩列表.整数 ...

  2. 图解Redis之数据结构篇——链表

    前言     Redis链表为双向无环链表!     图解Redis之数据结构篇--简单动态字符串SDS提到Redis使用了简单动态字符串,链表,字典(散列表),跳跃表,整数集合,压缩列表这些数据结构 ...

  3. 图解Redis之数据结构篇——简单动态字符串SDS

    图解Redis之数据结构篇--简单动态字符串SDS 前言     相信用过Redis的人都知道,Redis提供了一个逻辑上的对象系统构建了一个键值对数据库以供客户端用户使用.这个对象系统包括字符串对象 ...

  4. 探索Redis设计与实现6:Redis内部数据结构详解——skiplist

    本文转自互联网 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutorial ...

  5. 探索Redis设计与实现7:Redis内部数据结构详解——intset

    本文转自互联网 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutorial ...

  6. 探索Redis设计与实现5:Redis内部数据结构详解——quicklist

    本文转自互联网 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutorial ...

  7. 探索Redis设计与实现4:Redis内部数据结构详解——ziplist

    本文转自互联网 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutorial ...

  8. 探索Redis设计与实现3:Redis内部数据结构详解——sds

    本文转自互联网 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutorial ...

  9. 探索Redis设计与实现2:Redis内部数据结构详解——dict

    本文转自互联网 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutorial ...

随机推荐

  1. Flask-Babel 中文支持(zh-CN和zh-Hans-CN)

    命名的翻译文件夹必须命名为zh_Hans-CN,其他的都是不标准的命名!

  2. ELK——Logstash 2.2 date 插件【翻译+实践】

    官网地址 本文内容 语法 测试数据 可配置选项 参考资料 date 插件是日期插件,这个插件,常用而重要. 如果不用 date 插件,那么 Logstash 将处理时间作为时间戳.时间戳字段是 Log ...

  3. 内部通信服务Factory(WCF)

    WCF,很好,却又麻烦,很多时候不想用WCF的原因就是:用这个真麻烦... 麻烦的地方,比如: 一堆一堆的服务配置,散落在一个一个的folder下,更新系统时容易出错 客户端除了要知道WCF Cont ...

  4. 删除数据报ORA-00600: internal error code, arguments: [ktbesc_plugged]

    Oracle在删除数据是以下错误: ORA-00600: internal error code, arguments: [ktbesc_plugged], [], [], [], [], [], [ ...

  5. 为什么 NSLog 不支持 Swift 对象(转)

    https://segmentfault.com/a/1190000005668218 也就说: 1. 如果是 OC 的对象,重写 description 的 get 方法就可以了. 2. 如果不是 ...

  6. 如何优雅的在MFC中使用cvSetMouseCallback?

    MFC与opencv的cvSetMouseCallback用起来感觉很不兼容. 大部分时候,用cvSetMouseCallback也许只是为了获取一个矩形框,或者绘制一个点,或者其它什么简易的图形,通 ...

  7. dll return a string

    char g_szText[1024]; __declspec(dllexport) const char * __stdcall FuncName(const unsigned char *p) { ...

  8. phalcon开发工具(phalcon-devtools)

    一.简介 Phalcon提供的这个开发工具主要是用来辅助开发,比如生成一些程序的基本框架,生成控制器模型等.使用这个工具我们只需要一个简单的命令即可生成应用的基本框架. 二.下载 github: ht ...

  9. solr集成mmseg4j分词

    solr集成mmseg4j分词 mmseg4j https://code.google.com/p/mmseg4j/ https://github.com/chenlb/mmseg4j-solr 作者 ...

  10. easyui plugin——etreegrid:CRUD Treegrid

    昨天写了一个ko+easyui的同样的实现,感觉写的太乱,用起来十分麻烦,于是今天照着edatagrid,写了一个etreegrid,这样再用ko绑定就方便多了. 使用很简单,$(tableId).e ...