当前位置: 首页 > 后端技术 > PHP

为什么redis的整数集不能降级

时间:2023-03-29 18:30:55 PHP

前言相信有些同学没有听说过整数集,因为redis只提供了5个封装对象!我们系列的主要目的是学习redis的内部结构。内部结构是redis的5大结构的重要支撑!前面我们从redis的内部结构分析了redis的List、Hash、Zset这三种数据结构。今天我们就来分析一下集合数据结构的基本结构是如何存储的。在src/t_set.c中,我们找到了这样一段代码。由此我们可以知道集合是由两种数据结构组成的:hashtable+intset。关于redis的其他内部结构,我已经在【redis专栏】中介绍过了。Hashtable不是我们今天的主角。我们来分析一下intset,俗称整数集。从上图中,我们可以看到我构建了两个集合,分别命名为[commonset]和[cs]。两个集合中前者存储字符串,后者专门存储数字。我们通过objectencodingkey查看下两个集合的底层数据结构,发现一个是hashtable,一个是intset。这也验证了我们上面对set基本结构的描述。redis对外提供的五大类型,其实都是redis的一个抽象对象,叫做redisobject。我们redis的内部数据结构是内部映射的。对于commonset和cs,内部的数据结构可以大致这样理解。在使用intset的时候,可以简单的认为只要是数字,都会使用intset结构进行存储。这是一个打击。其实不是这样的,需要同时满足以下两个条件:intset图很清楚,intset中的encoding有三个代表内容存储数据类型的值。这里可能有人会有疑问,内容的类型不就是int8_t吗?为什么我们需要编码?在这里,通过源代码追踪内部结构与int8_t无关。并且默认的数据类型是int16_t。关于长度这里就不用过多解释了。请记住,contents中的元素数量并不意味着contents数组的长度!了解intset的同学都知道,upgrade操作涉及encoding的三个取值范围!在谈升级之前,我们先了解一下C和C++中int的取值范围是如何定义的。int8_t的取值范围是[-128,127]。类似于java中的byte,占用1个字节,也就是8位。其取值范围为?27~27?1,即?128~127?27~27?1,即?128~127,加入元素saddjuejin-123saddjuejin-6saddjuejin12saddjuejin56saddjuejin321juejin是钥匙里面的插页。上面我们添加了5个元素,这5个元素的长度都在16以内!所以当前intset的编码=INTSET_ENC_INT16。-123占据内容的前16个位置。因此,当前5个元素内容的长度为16*5=80;注意set在存储int类型数据时,内部是按照从小到大的顺序存储的。不知道你有没有考虑过上面的typechange问??题,或者你有没有遇到过!intset默认是int16,就像我们上面添加的五个元素一样。这时,我们添加第六个元素,即65535(32位)。那么这时候16位的长度已经不够存储了,这时候intset怎么办呢!另外,我们添加第六个元素,删除65535之后,结构是不是和添加之前一样呢!让我们带着这两个问题来一探究竟吧!!!升级首先,我们来看第一个问题。原来这5个元素都是16位的,此时加上的65535是32位的长度。那么是不是可以直接把32位相加赋值给65535呢?答案肯定不是,首先直接追加不能保证数组元素大小的顺序!其次,如果前五位是16位,第六位是32位,那么intset结构中就没有多余的域可以标记了。也就是说在解析的时候,无法判断应该解析16位还是32位。为了解析方便,redis会在加高长度的时候升级整个内容。意思是先把全部内容展开,然后加上65535重新填充数据。首先,根据长度可以确定展开后的元素个数为6,每个占32,所以contents的长度为32*6=192。此时前80位的内容保持不变。旧数据移位开辟出足够的空间后,我们就可以对旧数据进行移位了。这里我们从原数组的末尾开始移动。在移动之前,我们需要明确在新数组中的位置。排序位置。此时,我们先比较321,确定他在新数组中的排名是第五,那么他将在新内容中占据128~159的范围。最终将移动前5个元素。最后,填写新添加的元素。当发生升级时,一定是因为新元素的长度大于原来的长度。那么他的值肯定在新数组的两端。负数在最左边,正数在最右边降级。接下来的问题是redis删除新添加的65535怎么办。这时候元素长度其实是16位,但是此时encoding却是32位。按照我的意见,应该实施降级!但不幸的是redis没有,所以请想想为什么不呢?如果你让你意识到你将如何实现,为什么不实施降级?当添加的元素超过当前长度时,我们很容易知道此时需要进行升级操作,但是当我们删除一条数据时,如何判断是否需要降级呢?难度很大,需要重新迭代剩余元素是否小于当前长度,实现复杂度O(N)。这是不执行降级的原因之一。你可能会说反正反遍也很快,反正在内存里。你有没有想过,如果遇到降级后升级,来回升级降级会降低我们程序的性能?表现。我们知道升级是必须的,所以这里redis的降级就是无视攻略总结