前言

整数集合相信有的同学没有听说过,因为redis对外提供的只有封装的五大对象!而我们本系列主旨是学习redis内部结构。内部结构是redis五大结构重要支撑!

前面我们分别从redis内部结构分析了redis的List、Hash、Zset三种数据结构了。今天我们再来分析set数据结构内部是如何存储的

基本结构

  • 在src/t_set.c中我们发现这样一段代码

  • 由此我们可知在set中是由两种数据结构构成的: hashtable+intset 。关于redis内部其他的结构我专门在【redis专栏中有介绍】。hashtable不是我们今天的主角,我们今天先分析intset俗称整数集合。

  • 从上图中我们可以看出,我构造了两个set集合分别为【commonset】、【cs】。两个集合前者存储字符串、后者专门存储数字。

  • 我们在通过object encoding key 来查看下两个集合的底层数据结构,发现一个是hashtable 一个是intset 。这也验证了我们上面对set基本结构的描述。

  • 在redis中对外提供五大类型实际上都是redis的一个抽象对象叫做redisobject。在内部映射了我们redis内部的数据结构

  • 针对commonset和cs两个集合在内部数据结构大概可以这么理解

何时使用intset

  • 你可以单纯的认为只要是数字就会使用intset结构来存储,我恐怕要给你当头一棒了。实际上并不是这样

  • 需要同时满足以下两个条件:

intset

  • 图中表示的很清楚了,在intset中的encoding有三种取值分别代表contents保存数据类型。这里有人可能会有疑问了contents的类型不就是int8_t吗?为什么还需要encoding呢?这里通过源码跟踪内部的确跟int8_t没啥关系。而且数据的默认类型就是int16_t 。关于length这里无需太多解释,记住一点表示contents元素的个数并非表示contents数组的长度!
  • 了解intset的同学都知道在encoding三种取值范围中涉及了升级的操作!在讲升级之前我们先来了解下C、C++中int的取值范围是如何定义的
  • int8_t的取值范围是【-128,127】 。 类似于java中byte占1个字节也就是8位。他的取值范围是
\[-2^{7} \sim 2^{7}-1 \\
即 \\
-128 \sim 127
\]

添加元素

sadd juejin -123
sadd juejin -6
sadd juejin 12
sadd juejin 56
sadd juejin 321
  • juejin这个key内部就是intset 。

  • 上面我们添加了5个元素且这五个元素的长度都在16之内!所以当前的intset的encoding=INTSET_ENC_INT16。-123在contents中占前16位。

  • 所以当前五个元素占contents的长度是16*5=80 ;

  • 注意set在存储int类型数据时,内部是按照从小到大的顺序存储的。

类型变动

  • 上面的问题不知道你有没有考虑过,或者说有没有遇到过!intset默认是int16位,正如我们上面添加的五个元素。加入此时我们添加第6个元素是65535(32位)。那么此时16位的长度就不够存储了这个时候intset会怎么做!
  • 另外当我们添加第6个元素后又将65535删除了之后,结构和添加之前是否一样!下面我们带着这两个问题来一探究竟!!!

升级

  • 首先我们针对第一问题来看看。原来五个元素都是16位就可以满足了,这个时候添加的65535是32位长度的。那么是不是可以直接追加32位分配给65535呢?
  • 答案是肯定不行,首先直接追加无法保证数组元素的大小顺序!其次如果前五个分别是16位,第6个是32位那么在intset结构中没有多余的字段来进行标记。也就是说在解析的时候就无法判断应该解析16位还是32位了.
  • redis为了方便解析所以在有高长度加入时会将整个contents进行升级。意思就是将整个contents先进行扩容,然后在重新填充数据

加入65535

  • 首先根据length可以确定扩容后元素个数为6 , 每个占位32,所以contents长度为32*6=192 。 此时前80位内容保持不变

旧数据移位

  • 开辟了足够的空间后,我们就可以对旧数据进行移位了这里我们从原数组的末尾开始移动,在移动之前需要明确在新数组中的排序位置。
  • 此时我们首先将321进行比对确定在新数组中他的排名是第五名,那么他将占用新contents中128~159区间。

  • 最终前5 个元素就会被移动好 。

  • 最后将新加入的元素填充进去。当发生升级时肯定是因为新元素的长度大于原有长度了。那么他的值一定会是在新数组的两端。负数在最左侧,正数在最右侧

降级

  • 接下来就是第二个问题当新加入的65535又被删除了redis该怎么办,这个时候元素长度实际16位就可以满足了,但是此时encoding却是32位的。按照我的看法应该在实现降级!
  • 但是遗憾的是redis并没有,那么请思考为什么没有?如果让你实现你将如何实现

为什么不实现降级

  • 当加入元素超过当前长度我们很容易就知道此时需要进行升级操作,但是当我们删除一个数据时我们如何判断是否需要降级却很困难,我们需要重新遍历一遍剩下的元素是否小于当前长度,实现复杂度O(N) 。这就是为什么不进行降级原因之一
  • 你可能会说重新遍历一遍很快的反正在内存中,那么你有没有想过如果降级之后又遇到升级情况,这样来回的升级降级就降低了我们程序的性能了。我们知道升级是必须的所以这里降级redis采取的是忽略的策略

小结

参考资料:内存升级优化内存降级

【redis前传】redis整数集为什么不能降级的更多相关文章

  1. 【redis前传】自己手写一个LRU策略 | redis淘汰策略

    title: 自己手写一个LRU策略 date: 2021-06-18 12:00:30 tags: - [redis] - [lru] categories: - [redis] permalink ...

  2. 【redis前传】集思广益之quicklist,取其精华去其糟粕

    前言 在之前我们已经学习了redis五大数据结构中的list结构.其内部是linkedList和zipList两种结构.这是我们已经学习的内容.之前我没有结合操作具体查看.事实上在两者中还存在一种结合 ...

  3. redis主从架构,分片集群详解

    写在前面:这篇笔记有点长,如果你认真看完,收获会不少,如果你只是忘记了相关命令,请翻到末尾. redis的简单介绍: 一个提供多种数据类类型储存,整个系统都在内存中运行的, 定期通过异步的方式把数据刷 ...

  4. Redis 安装、配置、集群

    1.   Redis的安装 1.1. Redis的安装 Redis是c语言开发的. 安装redis需要c语言的编译环境.如果没有gcc需要在线安装.yum install gcc-c++ 安装步骤: ...

  5. 快速搭建redis单机版和redis集群版

    单机版 第一步:需要安装redis所需的C语言环境,若虚拟机联网,则执行 yum install gcc-c++ 第二步:redis的源码包上传到linux系统 第三步:解压缩redis   tar ...

  6. Redis高可用-主从,哨兵,集群

    主从复制 Master-Slave主从概念 同时运行多个redis服务端,其中一个作为主(master),其他的一个或多个作为从(slave),主从之间通过网络进行通讯,slave通过复制master ...

  7. Redis-cluster集群【第一篇】:redis安装及redis数据类型

    Redis介绍: 一.介绍 redis 是一个开源的.使用C语言编写的.支持网络交互的.可以基于内存也可以持久化的Key-Value数据库. redis的源码非常简单,只要有时间看看谭浩强的C语言,在 ...

  8. redis的hash操作在集中式session中的应用

    在集群部署时,为了高可用性的目的,往往把session进行共享,共享分为两种:session复制和集中式管理. redis在session集中式管理中可以起到比较大的作用. 制约session集中式共 ...

  9. Redis【第二篇】集群搭建

    第一步:准备 1.安装包 ruby-2.4.0.tar.gz rubygems-2.6.10.tgz zlib-1.2.11.tar.gz redis-3.3.2.gem 2. 架构: 名称 IP 端 ...

随机推荐

  1. CUDA刷新器:CUDA编程模型

    CUDA刷新器:CUDA编程模型 CUDA Refresher: The CUDA Programming Model CUDA,CUDA刷新器,并行编程 这是CUDA更新系列的第四篇文章,它的目标是 ...

  2. Waymo的激光雷达计划:进展如何?

    Waymo的激光雷达计划:进展如何? Waymo's Lidar Plan: How's It Working out? 许多自动驾驶汽车(AV)开发商一直在热烈追求激光雷达技术,这一技术之所以重要, ...

  3. Centos8配置Nginx开机自启动

    第一步:创建service文件,并编辑(可理解为开机时自动启动Nginx的脚本服务文件) vim /lib/systemd/system/nginx.service /lib 与 /usr/lib 里 ...

  4. (3)虚拟Web主机

    虚拟Web主机 作用:让一台Web服务器,提供多个页面 搭建方式: 1.基于域名的虚拟Web 2.基于端口的虚拟Web 3.基于IP地址的虚拟Web ######################### ...

  5. Java调试大法,来了~

    很多同学经常问我:彤哥,你的源码为什么讲的那么好那么细,有没有什么方法? 此时,我一般回复四个字:调试大法. 然后,他们就会很懵逼:调试我也会呀,但是,我就做不到你那么细(像是在夸我),难道调试还有我 ...

  6. Redis分布式锁 (图解-秒懂-史上最全)

    文章很长,而且持续更新,建议收藏起来,慢慢读! 高并发 发烧友社群:疯狂创客圈(总入口) 奉上以下珍贵的学习资源: 疯狂创客圈 经典图书 : 极致经典 + 社群大片好评 < Java 高并发 三 ...

  7. Perm 排列计数

    题目描述 称一个1,2,...,N的排列P1,P2...,Pn是Magic的,当且仅当2<=i<=N时,Pi>Pi/2. 计算1,2,...N的排列中有多少是Magic的,答案可能很 ...

  8. Python变量小秘密

    变量全都是引用 跟其他编程语言不同,Python的变量不是盒子,不会存储数据,它们只是引用,就像标签一样,贴在对象上面. 比如: >>> a = [1, 2, 3] >> ...

  9. 《手把手教你》系列基础篇之(二)-java+ selenium自动化测试-环境搭建(下)基于Maven(详细教程)

    1.简介 Apache Maven是一个软件项目管理和综合工具.基于项目对象模型(POM)的概念,Maven可以从一个中心资料片管理项目构建,报告和文件.由于现在企业和公司中Java的大部分项目都是基 ...

  10. Java8-四个函数式接口(Consumer,Supplier,Predicate,Function)

    Java8---函数式接口 Consumer---消费者(accept方法,Lambda与方法引用返回都是Consumer) Supplier---供给型(get方法,返回数据,与Optional可以 ...