整数集合

Redis 中当一个集合(set)中只包含整数,并且元素不多时,底层使用整数集合实现,否则使用字典实现。

那么:

  • 为什么会出现整数集合呢?都使用字典存储不行吗?
  • 整数集合在 Redis 中的结构是怎样的呢?
  • 引入整数集合之后,会不会出现什么弊端?Redis 又是如何去解决的呢?

为什么会出现整数集合呢?都使用字典存储不行吗?

说起无序集合(set),很自然的就想起哈希表,而哈希表表现也很好,提供了查询为 O(1) 的时间复杂度。但任何事物有优点,就必然会伴随着一些缺点,哈希表的缺点就是使用的储存空间大,其实哈希表本身就是空间换取时间的一种方式。

Redis 作为一个缓存,考虑到内存的使用,所以出现了整数集合这一数据结构。然而整数集合虽然相比哈希表很节约内存,但它也是牺牲了速度作为代价的。所以 Redis 中也只是在数据量少的条件下才会去使用整数集合代替哈希表。

整数集合在 Redis 中的结构是怎样的呢?

Redis 中的整数集合由 intset.h/intset 表示,具体结构如下:

typedef struct intset {

    // 编码方式
uint32_t encoding; // 集合包含的元素数量
uint32_t length; // 保存元素的数组
int8_t contents[]; } intset;

length属性表示整数集合包含的元素数量,即是contents数组的长度。

contents 数组是整个整数集合最核心的部分,元素就存储在这个数组中,各个元素从小到大有序排列,并且没有重复元素。虽然 contents 数组是声明成int8_t的类型,但是存储元素的真正类型并不是 int8_t 而是由 encoding 属性来表示 contents 数组真正的类型。比如:

  • 如果 encoding 属性的值为 INTSET_ENC_NT16,那么 contents 就是一个 int16_t 类型的数组,数组里的每个项都是一个 int16_t 类型的整数值(最小值为 -32768,最大值为 32767)。
  • 如果 encoding 属性的值为 INTSET_ENC_NT32,那么 contents 就是一个 int32_t 类型的数组,数组里的每个项都是一个 int32_t 类型的整数值(最小值为 -2^31,最大值为 2^31-1)。

引入整数集合之后,会不会出现什么弊端?

当我们向集合中插入元素时,Redis 并没有让我们指定插入的数值是几个字节的,那么如果我们先插入了 int16_t 的元素,然后插入了一个 int32_t 的元素,那么这时候原来的 contents 存不了新插入的值,这个时候该怎么做呢? 这也就是 Redis 引入整数集合之后所要解决的问题。

Redis 为了解决这个问题,于是引入了升级。

升级

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

  1. 根据新元素的类型,扩展整数集合底层数组的空间大小,并为新元素分配空间。
  2. 将底层数组现有的所有元素都转换成与新元素相同的类型,并将类型转换后的元素
  3. 将新元素添加到底层数组(contents)里面。

升级的时间复杂度是 O(n),又每次向整数集合添加新元素都可能引发升级,所以整数集合添加新元素的时间复杂度是 O(n)。

而为整数集合引入升级之后,主要带来了两点好处:

第一:提升了程序的灵活性。

第二:节约内存。

看到这里,可能有的人会想,有了升级,那么有没有降级呢?很遗憾,Redis 中的整数集合没有降级,也就是说只能扩容,不能缩容。

压缩列表

本篇只是简单介绍一下压缩列表,所以暂不深入讲解,感兴趣的可以自行查阅其他资料。

Redis 在什么情况下会使用压缩列表作为底层实现呢?

  1. 列表只包含少量元素,并且每个列表项要么是小整数值,要么是长度较短的字符串。
  2. 哈希键中只包含少量键值对,并且每个键值对的键和值要么是小整数值,要么是长度较短的字符串。

其实看到压缩二字,就能联想到节约内存。所以本篇介绍的整数集合和压缩列表都是为了节约内存而存在的。那么,压缩列表的大致结构是什么?

压缩列表的大致结构

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

它的各个组成部分和说明如下(出自《Redis设计与实现第二版》第7章:压缩列表):

压缩列表更像是一种存储方式,和 Java 字节码中常量池的存储比较类似。感兴趣的可以去查一下。

压缩列表的节点主要由 previous_entry_length、encoding、content 三部分组成。如下图(出自《Redis设计与实现第二版》第7章:压缩列表):

  • previous_entry_length 表示压缩列表中前一个节点的长度,单位为字节。
  • encoding 记录了节点的 content 属性所保存数据的类型及长度。
  • content 则负责保存节点的值。可以使一个字节数组(字符串)或者整数。

Redis 学习笔记(篇四):整数集合和压缩列表的更多相关文章

  1. Redis学习笔记(四)集合进阶

    1.组合与关联多个集合 差集: SDIFF key1 [key2...](返回存在于key1但不存在其他集合中的元素) SDIFFSTORE destination key1 [key2...](将存 ...

  2. Redis底层探秘(四):整数集合及压缩列表

    整数集合 整数集合(intset)是集合键的底层实现之一,当一个集合只包含 整数值元素,并且这个集合的元素数量不多时,Redis就会使用郑书记和作为集合键的底层实现. 整数集合的实现 整数集合是red ...

  3. Redis数据结构—整数集合与压缩列表

    目录 Redis数据结构-整数集合与压缩列表 整数集合的实现 整数集合的升级 整数集合不支持降级 压缩列表的构成 压缩列表节点的构成 小结 Redis数据结构-整数集合与压缩列表 大家好,我是白泽.今 ...

  4. redis 笔记01 简单动态字符串、链表、字典、跳跃表、整数集合、压缩列表

    文中内容摘自<redis设计与实现> 简单动态字符串 1. Redis只会使用C字符串作为字面量,在大多数情况下,Redis使用SDS(Simple Dynamic String,简单动态 ...

  5. 【转】Redis学习笔记(四)如何用Redis实现分布式锁(1)—— 单机版

    原文地址:http://bridgeforyou.cn/2018/09/01/Redis-Dsitributed-Lock-1/ 为什么要使用分布式锁 这个问题,可以分为两个问题来回答: 为什么要使用 ...

  6. Redis学习笔记(四)——数据结构之List

    一.介绍 Redis列表(List)是简单的字符串列表,按照插入顺序排序.你可以添加一个元素到列表的头部(left)或者尾部(right),一个列表最多可以包含232-1个元素(4294967295, ...

  7. Redis 学习笔记(四)RDB 和 AOF 持久化机制

    一.Redis 持久化简介 Redis 的持久化功能是区别于 Memcached 显著特性,数据持久化可以保证系统在发生宕机和重启后数据不会丢失,对于 redis 这种存储在内存中的数据库显得尤为重要 ...

  8. Redis学习笔记(四)-数据类型之list类型

    redis的list类型其实就是一个每个子元素都是string类型的双向链表.所以[lr]push和[lr]pop命令的算法时间复杂度都是O(1).另外list会记录链表的长度.所以llen操作也是O ...

  9. Redis学习笔记(四) 基本命令:String操作

    原文链接:http://doc.redisfans.com/string/index.html append key value 将指定的值追加到key末尾,若key不存在,则创建并赋值,返回追加后的 ...

随机推荐

  1. NYOJ 298 相变点(矩阵高速功率)

    点的变换 时间限制:2000 ms  |  内存限制:65535 KB 难度:5 描写叙述 平面上有不超过10000个点.坐标都是已知的.如今可能对全部的点做下面几种操作: 平移一定距离(M),相对X ...

  2. WPF学习笔记:(二)数据绑定模式与INotifyPropertyChanged接口

    数据绑定模式共有四种:OneTime.OneWay.OneWayToSource和TwoWay,默认是TwoWay.一般来说,完成数据绑定要有三个要点:目标属性是依赖属性.绑定设置和实现了INotif ...

  3. 【WPF】右下角弹出自定义通知样式(Notification)——简单教程

    原文:[WPF]右下角弹出自定义通知样式(Notification)--简单教程 1.先看效果 2.实现 1.主界面是MainWindow 上面就只摆放一个Button即可.在Button的点击事件中 ...

  4. Plupload上传实例《模仿微云上传实例》,带源码

    Plupload上传实例<模仿微云上传实例>,带源码,作者:鱼塘总裁 如有疑问,加群交流:646104701 一.实例截图 1.上传过程 2.上传成功 3.上传失败 4.最小化 二.所需文 ...

  5. 在WPF中实现图片一边下载一边显示

    原文 在WPF中实现图片一边下载一边显示 当我们上网查看一个较大的图片时,浏览器能一边下载一边显示,这样用户体验是比较好的,但在WPF程序中,当我们通过如下方式显示一幅图片时: img.Source ...

  6. javascript控制rem字体大小

    摘要:在写响应式H5页面的时候,我常常会用rem字体,为了兼容多个分辨率的设备,需要写多个@media标签,太麻烦并且不够精致,尤其是移动端的页面往往达不到我想要的效果,后来就用js替代了css的@m ...

  7. UWP中的消息提示框(一)

    不管什么平台,应用内难免会出现一些消息提示框,下面就来聊聊我在UWP里用到的消息提示框. 弹窗也可按是否需要用户操作促发一些逻辑进行分为两大类. 不需要用户干涉的一类: MessageDialog:操 ...

  8. 在Delphi中创建线程,请一定使用BeginThread()代替CreateThread()创建线程!(更好的管理异常)

    在Delphi中创建线程,请一定使用BeginThread()代替CreateThread()创建线程! 如果直接使用Win32的API函数CreateThread()创建多个线程,也是可以创建的.但 ...

  9. Delphi 导出数据至Excel的7种方法

    一; delphi 快速导出excel uses ComObj,clipbrd; function ToExcel(sfilename:string; ADOQuery:TADOQuery):bool ...

  10. OSChina 周三乱弹 —— 致力于做一名优秀的女程序员鼓励师

    https://my.oschina.net/xxiaobian/blog/848096