文章目录
- 🍊 简单字符串
- 🎉 问题1:SDS结构体的三个属性分别表示什么意思?
- 🎉 问题2:SDS字符串的内存分配方式是怎么样的?
- 🎉 问题3:SDS字符串的拼接操作是怎么样的?
- 🎉 问题4:什么场景下使用它?怎么优化它?
- 🍊 链表
- 🍊 跳跃表
- 🍊 字典
- 🍊 压缩列表
- 🎉 压缩列表的结构
- 🎉 压缩列表的节点
- 📝 1. 字符串类型
- 📝 2. 整数类型
- 📝 3. 列表节点
- 📝 4. 压缩表节点
- 🎉 压缩列表的优缺点
- 🎉 压缩列表的应用
- 🍊 整数集合
- 🎉 1. 内存占用
- 🎉 2. 添加和删除操作
- 🎉 3. 查询操作
📕我是廖志伟,一名Java开发工程师、Java领域优质创作者、CSDN博客专家、51CTO专家博主、阿里云专家博主、清华大学出版社签约作者、产品软文创造者、技术文章评审老师、问卷调查设计师、个人社区创始人、开源项目贡献者。🌎跑过十五公里、徒步爬过衡山、🔥有过三个月减肥20斤的经历、是个喜欢躺平的狠人。
📘拥有多年一线研发和团队管理经验,研究过主流框架的底层源码(Spring、SpringBoot、Spring MVC、SpringCould、Mybatis、Dubbo、Zookeeper),消息中间件底层架构原理(RabbitMQ、RockerMQ、Kafka)、Redis缓存、MySQL关系型数据库、 ElasticSearch全文搜索、MongoDB非关系型数据库、Apache ShardingSphere分库分表读写分离、设计模式、领域驱动DDD、Kubernetes容器编排等。🎥有从0到1的高并发项目经验,利用弹性伸缩、负载均衡、报警任务、自启动脚本,最高压测过200台机器,有着丰富的项目调优经验。
📙经过多年在CSDN创作上千篇文章的经验积累,我已经拥有了不错的写作技巧。同时,我还与清华大学出版社签下了四本书籍的合约,并将陆续在明年出版。这些书籍包括了基础篇、进阶篇、架构篇的📌《Java项目实战—深入理解大型互联网企业通用技术》📌,以及📚《解密程序员的思维密码–沟通、演讲、思考的实践》📚。具体出版计划会根据实际情况进行调整,希望各位读者朋友能够多多支持!
希望各位读者大大多多支持用心写文章的博主,现在时代变了,信息爆炸,酒香也怕巷子深,博主真的需要大家的帮助才能在这片海洋中继续发光发热,所以,赶紧动动你的小手,点波关注❤️,点波赞👍,点波收藏⭐,甚至点波评论✍️,都是对博主最好的支持和鼓励!
- 💂 博客主页: 我是廖志伟
- 👉开源项目:java_wxid
- 🌥 哔哩哔哩:我是廖志伟
- 🎏个人社区:幕后大佬
- 🔖个人微信号:
SeniorRD
💡在这个美好的时刻,本人不再啰嗦废话,现在毫不拖延地进入文章所要讨论的主题。接下来,我将为大家呈现正文内容。
🍊 简单字符串
SDS的优势主要在于解决了C语言处理字符串中的一些问题,例如长度和内存重新分配问题以及结尾标识问题。因为SDS有len属性和free属性记录字符串长度和剩余空间,所以可以避免C语言需要遍历来计算字符串长度的问题,也可以避免每次修改字符串都需要重新分配内存的问题,因为它可以通过检查len属性判断是否需要扩展内存,并且有free属性来记录剩余空间用于后续操作。而SDS以len作为结尾标识,避免了C语言中空字符串结尾标识可能中途截断字符串的问题。
举个例子,如果我们需要在字符串末尾添加一个字符,在C语言中需要重新分配内存并将原有数据复制到新的内存中,而对于SDS则只需要判断是否有足够的空闲空间,如果没有则扩展内存,并将新字符添加到字符串末尾即可,不需要重新分配内存和复制数据。
另外,SDS还提供了一些其他的优势,例如二进制安全(因为不是以空字符串结尾),支持O(1)复杂度的字符串长度计算和拼接操作等。
举个例子来说,假设我们有一个字符串“Hello World!”。如果使用char*来表示它,我们需要手动计算字符串长度,并为其准备缓冲区。这个过程很容易出错,而且无法动态地调整字符串大小。但是,如果我们使用SDS来存储这个字符串,我们只需要调用一个函数就可以获取字符串长度,而且在需要更改字符串大小时,SDS会自动地为其准备缓冲区,不需要我们手动操作。这样可以大大地减少程序员出错的可能性,同时也提高了代码的可维护性。
🎉 问题1:SDS结构体的三个属性分别表示什么意思?
SDS结构体的三个属性分别是len、free和buf[]。
- len表示SDS字符串的长度,也就是实际存储的字符个数,不包含结尾的空字符。
- free表示SDS字符串的剩余空间,也就是还可以存储的字符个数,单位是字节。
- buf[]是实际存储SDS字符串的字符数组,它的长度是len+free。
举个例子,如果我们创建一个长度为5的SDS字符串“hello”,那么它的len为5,free则取决于SDS内存分配策略和实际使用情况,buf[]则是一个长度为10(5+5)的字符数组,其中前5个元素存储了字符串“hello”的内容,后面5个元素未使用。
🎉 问题2:SDS字符串的内存分配方式是怎么样的?
SDS字符串的内存分配方式主要有两种,一种是预分配内存,另一种是惰性扩展内存。
预分配内存是指在创建SDS字符串时为它分配足够的内存空间以存储预估的最大字符串长度。例如,我们可以为一个长度为15的字符串预分配20个字节的空间,这样在字符串长度增加时就可以直接修改buf[]中的内容,而不需要每次都重新分配内存。
惰性扩展内存是指在SDS字符串长度增加时,只分配刚好能够存储新字符的空间,而不是直接分配预估的最大字符串长度。例如,我们可以为一个长度为15的字符串惰性扩展5个字节的空间以存储一个新字符,这样可以节省内存空间的使用。
SDS字符串支持这两种内存分配方式的原因是因为它有free属性记录剩余空间,在进行字符串修改时可以通过检查free是否足够来决定是否需要分配更多的空间。如果free不足,则可以使用预分配的内存空间扩展SDS字符串,如果free足够,则可以使用惰性扩展内存的方式在buf[]中存储新字符。
🎉 问题3:SDS字符串的拼接操作是怎么样的?
SDS字符串的拼接操作是通过调用SDS库中提供的API函数来实现的,例如sdscat、sdscatprintf等函数。这些函数可以将一个SDS字符串和另一个SDS字符串、C字符串、整数等拼接在一起,并返回一个新的SDS字符串。
例如,我们可以使用sdscat函数将一个长度为5的SDS字符串“hello”和另一个长度为6的SDS字符串“world!”拼接在一起,得到一个长度为12的新SDS字符串“hello world!”。实现代码如下:
sds s1 = sdsnew("hello");
sds s2 = sdsnew(" world!");
sds s3 = sdscat(s1, s2);
printf("%s", s3); // 输出:hello world!
需要注意的是,SDS字符串的拼接操作在不分配新内存的情况下可以实现O(1)的时间复杂度,这是因为它可以通过free属性来检查是否有足够的剩余空间存储新字符串,而不需要像C语言一样重新分配内存和复制数据。
🎉 问题4:什么场景下使用它?怎么优化它?
Redis在很多场景下都会使用简单字符串这种数据结构,例如缓存应用、会话管理、分布式锁等等。因为简单字符串的存取速度很快,可以快速地读写数据,而且可以通过设置过期时间来实现缓存功能,使得数据可以随时被更新。另外,Redis还提供了一些操作简单字符串的命令,例如GET、SET、APPEND、INCRBY等等,使得对数据的操作变得非常方便。
在优化调优方面,对于简单字符串的使用,可以通过以下几个方面进行优化:
-
合理设置过期时间:如果使用简单字符串实现缓存功能,要注意设置合理的过期时间,避免数据一直占用内存。可以根据数据的使用频率和访问量来设置过期时间。
-
合理设置最大内存限制:可以设置Redis实例的最大内存限制,避免数据过多占用内存导致性能下降。可以根据实际情况设置限制,同时可以使用Redis提供的内存淘汰策略来回收不经常使用的内存。
-
尽量使用批量操作命令:尽量使用Redis提供的批量操作命令,例如MGET、MSET、DEL等等,避免频繁地发起单个操作命令,减少网络开销和CPU负载。
-
合理设置数据结构:如果数据比较复杂,可以考虑使用其他数据结构来存储数据,例如哈希表、有序集合、列表等等,避免使用简单字符串导致数据处理困难。
-
适当使用压缩功能:Redis提供了简单字符串的压缩功能,可以将长度较短的字符串进行压缩,减少内存占用。但是要注意,压缩操作会增加CPU负载,不适合频繁操作较大的字符串。
🍊 链表
链表是一种基本的数据结构,它不同于数组,数组的元素在内存中是连续存储的,链表的元素可以离散的存储在内存中的任意位置。链表是由一系列节点组成的,每个节点包含了数据和一个指向下一个节点的指针。通过这个指针,我们可以轻松地遍历整个链表。
比方说,你可以将链表看做是火车车厢,每个车厢内都有一个数据,车厢之间通过一根连接着的铁轨链接起来。你可以从一端开始,不停地往下走,每走一个车厢,就可以取出里面的数据,直到到达链表的尽头。
链表有很多种不同的类型,其中最基本的是单向链表。单向链表只有一个方向上的指针,每个节点只能指向它后面的一个节点。我们可以在链表的头部添加或删除节点,也可以利用指针在链表中任意位置插入或删除节点。
双端链表则相对于单向链表多了一个特性:对最后一个链接点的引用。我们可以在链表的头部或尾部添加或删除节点。
双向链表比单向链表更为灵活,每个节点有两个方向上的指针,它既可以从头部开始往后遍历,也可以从尾部开始往前遍历。在双向链表中,我们可以更加轻松地实现双向的查找和删除操作。
有序链表是一种特殊的链表,它的元素是按照一定的顺序排列的,比如从小到大或者从大到小。当我们添加元素时,可以按照一定的规则将元素插入到有序链表中的合适位置,这样就可以保证整个链表始终保持有序。
还有一种比较特殊的链表是有迭代器的链表,它的元素可以被遍历和访问。通过迭代器,我们可以方便地对链表中的元素进行操作,比如查找、插入、删除等操作。有迭代器的链表通常也被称作可遍历链表。
举个例子来说,假设我们需要在Redis中实现一个聊天室应用。在这个应用中,我们需要维护一个聊天记录的列表,并且支持分页查询和删除。如果我们使用数组来实现这个列表,当我们需要在列表的中间位置插入或删除元素时,就需要将数组中的元素进行移动,这样非常耗费时间。但是,如果我们使用链表来实现这个列表,我们只需要修改节点之间的指针,就可以实现快速插入和删除元素。此外,链表还可以实现高效的分页查询操作。
总之,链表是一种非常灵活的数据结构,可以用来存储各种类型的数据。在实际应用中,我们经常会用到链表来实现队列和栈等基本数据结构,也可以用来处理图像、音频和视频等大数据。了解链表的结构和特点,对我们理解和使用其他数据结构也会有很大的帮助。
🍊 跳跃表
跳跃表是一种基于有序链表的扩展数据结构,通过对链表建立索引的方式来提高数据的查找和插入效率。跳跃表的每个节点都有一个层高,层高范围在1到32之间,节点之间通过指针相连。跳跃表的建立是在原有的有序链表的基础上建立索引,每两个节点提取一个节点到上一级,抽出来的这一级就是索引,每个节点的层高都是随机生成的。
举例来说,假设现有一个长度为7的有序链表,节点值依次是1->3->4->5。如果要插入一个值是2的新节点,只需比较1,3,5即可确定值在1和3之间,减少了遍历的结点个数,提高了查找效率。如果再加一层索引,查找效率会进一步提高,但也会增加索引所占用的空间。这种链表加多级索引的结构就是跳跃表。
对于跳跃表的增删操作,插入的时间复杂度为O(logN),空间复杂度为 O(N),而删除的时间复杂度也为O(logN)。
在跳跃表中,当大量的新节点逐层比较并插入到原链表之后,上层的索引节点会不够用,这时需要选取一部分节点提到上一层。这里可以采用一种抛硬币的方法,即随机决定一个新节点是否选拔到上一层,每向上一层提拔的几率是50%。这是因为跳跃表的删除和添加节点是无法预测的,不能保证索引绝对分步均匀,但可以让大体趋于均匀。
跳跃表由两个结构组成:zskiplistNode和skiplist。zskiplistNode用于表示跳跃表节点,skiplist用于保存跳跃表节点的相关信息,比如节点的数量,以及指向表头节点和表尾节点的指针等等。在进行插入和删除操作时,跳跃表通过逐层比较和查找来确定要插入或删除的位置。如果要删除一层只剩下1个节点的索引层,则删除整个一层,但原链表除外。
举个例子来说,假设我们需要在Redis中实现一个基于分值排序的排行榜。在这个排行榜中,每个用户有一个分值,我们需要按照分值的大小来排序。如果我们使用数组来存储这个排行榜,排序的时间复杂度会比较高。但是,如果我们使用跳跃表来存储这个排行榜,我们可以将分值作为节点的分值,将用户ID作为节点的值,这样排序的时间复杂度就会降到O(log N),大大提高了排序效率。
Redis在以下场景中使用跳跃表:
-
排序:跳跃表是一种用于快速排序的数据结构,在Redis中,跳跃表被用于实现有序集合的底层数据结构,因为有序集合需要根据分值来排序。
-
频率统计:跳跃表也可以被用于统计元素出现的频率。在这种情况下,每个节点都包含了一个元素以及它在数据集中出现的频率。
在进行跳跃表的优化和调优时,可以从以下几个方面入手:
-
节点大小的优化:跳跃表的每个节点需要维护多重指针,因此节点的大小比较大,如果在处理大量数据时不进行优化,会导致内存占用过高。可以通过结合Redis的内存优化机制,使用Redis Object Encoding来压缩节点。
-
层数的优化:跳跃表的层数越高,它所占用的内存就越大,因此在实现跳跃表时要注意设置合理的层数。在Redis中,跳跃表的最大层数为32。
-
命令复杂度的优化:在Redis中,跳跃表的查询和插入操作的时间复杂度均为O(logN),由于Redis的数据集通常非常大,在插入和查询时会涉及大量节点,因此要注意优化这些命令的执行时间。
-
随机算法的优化:跳跃表的节点提升是基于随机算法的,因此随机算法的优化对性能提升有很大的影响。可以采用一些更为高效的随机算法来提升跳跃表的性能。
总之,跳跃表是一种高效的数据结构,在Redis中被广泛应用于有序集合和频率统计等场景。在实际使用中,需要根据实际情况进行优化和调优,以达到更高的性能和更低的内存占用。
🍊 字典
在计算机科学中,字典是一种常用的数据结构,用于快速查找和访问数据。通常,字典使用键(key)和对应的值(value)对数据进行存储,并且每个键都是唯一的。这意味着,在一个字典中,每个键都可以对应一个值,且每个值只与一个键相对应。
字典和哈希表是计算机科学中用于快速查找和访问数据的常用数据结构。Redis的字典使用哈希表实现,通过哈希函数把键映射为数组下标,从而确定存储位置。哈希冲突可以通过开放地址法或链地址法进行解决。而整数集合是保存整数值类型集合的一种方法,能够支持不同长度的整数类型,并且可以节省内存使用。
在C语言中,没有这种数据结构,因此Redis使用自己创造的字典数据结构。Redis的字典采用哈希表实现,它基于数组的key-value结构进行存储。通常,哈希表是一种把key映射为数组下标的数据结构。哈希表中的键值对被存储在数组中,通过哈希函数,把键值对中的键转换为数组下标,从而确定存储位置。
那么,哈希函数是什么呢?哈希函数就是把任意长度的输入(在这里是键)映射为固定长度的输出(在这里是数组下标)的函数。在C语言中,哈希函数通常使用模数运算符,例如把键的ASCII码加和然后对数组长度取模。
但是,一旦哈希函数把大的数字范围压缩到小的数字范围,就会出现哈希冲突。哈希冲突的原因是两个不同的键被映射到了同一个数组下标,这就意味着哈希表中的两个键值对的存储位置相同。为了解决哈希冲突,Redis使用了几种技术。
一种解决哈希冲突的方法是使用开放地址法,其中数组大小是存储数据的两倍,有一半的空间是空的。当冲突产生时,通过线性探测、二次探测以及再哈希法方法找到数组的一个空位,把键值对填入,不用哈希函数得到数组的下标。
线性探测中,如果哈希函数计算的原始下标是x,线性探测就是x+1,x+2,x+3,以此类推。而在二次探测中,探测的过程是x+1,x+4,x+9,x+16。这两种方式都可能产生聚集,即当哈希表快要满的时候,每插入一个新的键值对,就要频繁地探测插入位置,很多位置都被前面插入的数据占用了。
另一种解决哈希冲突的方法是使用链地址法,其中每个数据项都创建一个子链表或子数组。当产生冲突时,新的键值对直接存放到这个数组下标表示的链表中。
此外,Redis还使用整数集合来保存整数值类型的集合,保证元素不会重复。整数集合定义了三个属性:编码方式、集合包含的元素数量、以及保存元素的数组。这种方法可以极大地节省内存,并且能够支持不同长度的整数类型。
举个例子来说,假设我们要实现一个存储用户信息的数据结构。每个用户有一个用户名和一个密码,我们需要在这个数据结构中快速地根据用户名查找对应的密码。如果我们使用数组来存储这个数据结构,查询的时间复杂度会比较高。但是,如果我们使用字典来存储这个数据结构,我们可以将用户名作为键,将密码作为值,这样查询的时间复杂度就会降到O(1),大大提高了查询效率。
Redis中使用字典这种数据结构的场景非常广泛,主要包括以下几方面:
-
缓存:Redis中作为一个高速缓存系统,常常使用字典来存储缓存数据,以快速响应客户端请求。
-
计数器:当需要对某个计数器进行自增或自减操作时,可以使用字典来存储计数器的值,以实现高效的计数操作。
-
排行榜:Redis中可以使用字典来存储排行榜数据,以支持快速地根据分数进行排名。
在使用字典时,需要进行优化调优以提高性能和稳定性。具体来说,可以采用以下几种方法:
-
合理设置哈希表大小:哈希表的大小对性能和空间占用有重大影响。如果哈希表过小,会导致哈希冲突过多,影响查询性能;如果过大,则会浪费空间。因此,需要根据实际情况设置哈希表大小。
-
使用合适的哈希函数:哈希函数决定了键的哈希值,从而影响了查询性能和哈希冲突的数量。因此,需要选择适合自己数据集的哈希函数。
-
避免聚集:聚集是指哈希表中某个位置附近存在大量键值对,导致探测时性能下降。为避免聚集,可以考虑使用渐进式哈希等方法。
-
合理设置哈希表扩容机制:哈希表在插入元素过程中,可能会出现哈希冲突,造成空间不足。因此,在字典中需要实现一个扩容机制,当哈希表达到一定的负载因子时,自动扩容。需要合理设置哈希表扩容机制,以保证系统性能和稳定性。
-
设置适当的数据过期时间:字典作为缓存系统使用时,需要设置适当的数据过期时间,以避免数据过期而占用过多的内存空间。
-
使用压缩列表:Redis中的字典在某些情况下会使用压缩列表来存储数据,以减少内存使用和提高性能。
综上所述,使用字典这种数据结构需要根据具体的应用场景和数据特点进行优化调优,从而提高系统的性能、稳定性和可靠性。
🍊 压缩列表
压缩列表是一种特殊的数据结构,它由一组连续的内存块组成,实现了对数据的压缩存储。压缩列表在 Redis 中被广泛使用,例如在列表(list)、集合(set)和哈希表(hash)等数据类型中都广泛应用。它是 Redis 内部的一种数据结构,经过了优化和压缩,可以用来存储大量的数据,并且占用内存比较少。
举个例子来说,假设我们需要在Redis中存储一些小的键值对数据,比如一些配置信息。如果我们使用哈希表来存储这些数据,哈希表的空间开销比较大,而且在数据量比较小的时候,哈希表可能不太适用。但是,如果我们使用压缩列表来存储这些数据,它可以动态地调整自己的大小,而且在数据量比较小的时候,它可以更好地利用内存空间。
Redis 在以下场景中使用压缩列表这种数据结构:
-
列表(list):当列表元素数量比较少并且元素大小比较小的时候,使用压缩列表可以更好地利用内存空间。
-
集合(set):当集合元素数量比较少并且元素大小比较小的时候,使用压缩列表可以更好地利用内存空间。
-
有序集合(sorted set):当有序集合元素数量比较少并且元素大小比较小的时候,使用压缩列表可以更好地利用内存空间。
-
哈希表(hash):当哈希表元素数量比较少并且元素大小比较小的时候,使用压缩列表可以更好地利用内存空间。
在 Redis 内部,压缩列表是由多个节点构成的,每个节点包含一个指向前一个节点的指针、一个指向下一个节点的指针和一个数据区域。数据区域存储了节点中包含的数据。
为了优化和调优压缩列表,可以使用以下技术:
-
节点大小限制:对于压缩列表中的每个节点,可以限制其大小,以避免节点过大导致内存浪费。
-
内存回收机制:当压缩列表中的节点被删除时,可以触发内存回收机制,将已删除的节点所占用的内存空间重新利用起来。
-
紧凑化压缩列表:可以对压缩列表进行紧凑化操作,将多个连续的节点合并成一个节点,以减少压缩列表的空间占用。
-
压缩列表编码:压缩列表支持多种编码方式,可以根据存储数据的类型和大小,选择最合适的编码方式,以最大程度地减少空间占用。
🎉 压缩列表的结构
压缩列表由以下几个部分组成:
- zlbytes:这是压缩列表占用的总内存字节数,占用4个字节。
- zltail:这是表尾节点距离压缩列表的初始地址有多少字节,占用4个字节。
- zllen:这是压缩列表包含的节点数量,占用2个字节。
- zlend:用来标记压缩列表的末端,占用1个字节。
- entryX:压缩列表的节点,最大占用5个字节(前一个节点的长度占用1-5个字节,encoding占用1-4个字节,content占用1-64个字节)。
一个压缩列表的示意图如下:
+--------+--------+--------+--------+--------------+
| zlbytes| zltail | zllen | zlend | entry1 |
+--------+--------+--------+--------+--------------+
| 4 bytes| 4 bytes| 2 bytes| 1 byte | 1-5 bytes |
+--------+--------+--------+--------+--------------+
| entry2 | entry3 | ... | entryN | NULL |
+--------+--------+--------+--------+--------------+
在上面的示意图中,zlbytes、zltail、zllen、zlend是压缩列表的头部信息,entry1到entryN是压缩列表中包含的节点信息。
🎉 压缩列表的节点
压缩列表的每个节点又由三部分组成:previous_entry_length、encoding 和 content。
- previous_entry_length:表示上一个节点的长度,占用1-5个字节,如果当前节点是第一个节点,则这个字段为0。
- encoding:用来表示节点的类型和长度,占用1-4个字节。
- content:节点的内容,实际长度由encoding决定。
在encoding中,一个字节的高两位表示节点的类型,低六位表示节点的长度编码。节点的类型分为四种:
- 字符串类型(00)。
- 整数类型(01)。
- 列表节点(10)。
- 压缩表节点(11)。
对于不同的节点类型,encoding的长度表示的含义不同:
📝 1. 字符串类型
一个字符串节点的encoding如下所示:
+--------+--------+
| 00ssssss| len |
+--------+--------+
其中,00表示字符串节点,ssssss表示字符串的编码方式,len表示字符串的长度。可以看出,字符串的编码方式只占用了一个字节,而其它节点类型的编码方式比较复杂。
📝 2. 整数类型
一个整数节点的encoding如下所示:
+--------+--------+
| 01tttttt| ... |
+--------+--------+
其中,01表示整数节点,tttttt表示整数值的类型,后面的…是实际的整数值。整数值可以是8位、16位、32位或64位的整数,整数值的类型取决于encoding的值。
📝 3. 列表节点
一个列表节点的encoding如下所示:
+--------+--------+--------+--------+
| 10xxxxxx| len | ... | ... |
+--------+--------+--------+--------+
其中,10表示列表节点,xxxxxx表示上一个节点的长度(一般为7位),len表示列表节点包含的元素数量,…表示列表节点包含的元素,可以是字符串节点、整数节点或者其它类型的节点,这些元素的encoding、content格式都和上述一样。
📝 4. 压缩表节点
一个压缩表节点的encoding如下所示:
+--------+--------+--------+--------+
| 11ziplst| len | ... | ... |
+--------+--------+--------+--------+
其中,11表示压缩表节点,ziplst是一个固定的字符串,表示这是一个压缩表节点。len表示压缩表节点包含的字节数,…表示压缩表节点包含的数据,这些数据可能经过了压缩,也可能没有压缩。
🎉 压缩列表的优缺点
压缩列表有以下几个优点:
- 节省内存。压缩列表采用紧凑的内存布局,可以将多个节点存储在一个连续的内存块中,大大节省了内存空间。
- 效率高。由于压缩列表的节点是连续存储的,所以插入、删除、查找节点都非常高效。
- 支持多种类型的数据。压缩列表可以存储字符串、整数,以及列表、集合等复杂数据类型。
但是,压缩列表也存在一些缺点:
- 执行插入、删除操作时,内存的移动操作可能会很频繁,导致内存的效率降低。
- 压缩列表仅适用于小数据量,当节点数量超过一定的限制时,压缩列表的效率会降低。
- 压缩列表不支持范围查询操作,因为节点之间的距离是不确定的,无法快速跳过多个节点。
🎉 压缩列表的应用
压缩列表在 Redis 中被广泛应用,例如在列表、集合和哈希表等数据类型中都有应用。以列表为例,当列表中元素的个数比较少(少于512个)且每个元素的大小比较小(小于64字节)时,Redis就会使用压缩列表来存储列表元素。
在使用压缩列表时,需要注意以下几点:
- 为了保证压缩列表的效率,需要合理控制节点数和节点大小,一般建议不要超过512个节点和64字节的节点大小。
- 在使用压缩列表时,需要对节点的类型和长度进行合理的编码,这样才能提高压缩比,并且保证读写操作的效率。
- 在压缩列表的节点插入、删除、查找时,需要注意节点的前后关系和指针的变化,尤其是节点的插入和删除操作,需要注意内存的移动操作,以避免对性能的影响。
🍊 整数集合
整数集合是Redis中用于存储整数数据的核心数据结构,它底层由数组构成。整数集合支持升级特性,它可以尽可能地节省内存。当整数集合中存储的数据量比较小的时候,它只需要使用较小的数组空间。当数据量增多的时候,它会动态地升级为更大的数组,以便存储更多的数据。
举个例子来说,假设我们需要在Redis中存储一些整数数据,我们需要支持快速地添加、删除和查找操作。如果我们使用数组来存储这些整数数据,随着数据量的增加,我们需要不断地扩大数组的空间,这样会造成内存的浪费。但是,如果我们使用整数集合来存储这些整数数据,它可以自动地调整数组的大小,以便节省内存。
Redis使用整数集合这种数据结构主要是为了存储整数类型的数据。对于一些常见的场景,如计数器,用户ID等,整数集合是非常适合的数据结构。它可以快速地执行添加、删除和查找操作,同时也能够节省内存的使用。
优化整数集合的性能需要考虑以下几个方面:
🎉 1. 内存占用
整数集合在内存使用方面的优化主要集中在升级特性上。它能够动态地调整数组大小,以便尽可能地节省内存。如果整数集合中存储的数据量比较小,它只需要使用较小的数组空间。当数据量增多的时候,它会动态地升级为更大的数组,以便存储更多的数据。因此,我们不需要手动地分配内存,整数集合会自动处理。
🎉 2. 添加和删除操作
向整数集合中添加和删除元素的速度非常快。整数集合通过使用哈希表和有序数组的结合方式,能够快速地定位需要添加或删除的元素,并且能够快速地执行相应的操作。
🎉 3. 查询操作
整数集合能够快速地执行查询操作。整数集合内部使用了二分查找算法,在有序数组中快速查找元素。因此,查询操作的性能非常高。
总之,整数集合是Redis中非常重要的数据结构之一。它能够快速地执行添加、删除和查找操作,同时也能够节省内存的使用。在实际使用中,我们可以根据实际情况进行优化调优,以便获得更好的性能和更小的内存占用。
🔔如果您需要转载或者搬运这篇文章的话,非常欢迎您私信我哦~
希望各位读者大大多多支持用心写文章的博主,现在时代变了,信息爆炸,酒香也怕巷子深,博主真的需要大家的帮助才能在这片海洋中继续发光发热,所以,赶紧动动你的小手,点波关注❤️,点波赞👍,点波收藏⭐,甚至点波评论✍️,都是对博主最好的支持和鼓励!
- 💂 博客主页: 我是廖志伟
- 👉开源项目:java_wxid
- 🌥 哔哩哔哩:我是廖志伟
- 🎏个人社区:幕后大佬
- 🔖个人微信号:
SeniorRD
📥博主的人生感悟和目标
- 🍋程序开发这条路不能停,停下来容易被淘汰掉,吃不了自律的苦,就要受平庸的罪,持续的能力才能带来持续的自信。我本身是一个很普通程序员,放在人堆里,除了与生俱来的盛世美颜,就剩180的大高个了,就是我这样的一个人,默默写博文也有好多年了。
- 📺有句老话说的好,牛逼之前都是傻逼式的坚持,希望自己可以通过大量的作品、时间的积累、个人魅力、运气、时机,可以打造属于自己的技术影响力。
- 💥内心起伏不定,我时而激动,时而沉思。我希望自己能成为一个综合性人才,具备技术、业务和管理方面的精湛技能。我想成为产品架构路线的总设计师,团队的指挥者,技术团队的中流砥柱,企业战略和资本规划的实战专家。
- 🎉这个目标的实现需要不懈的努力和持续的成长,但我必须努力追求。因为我知道,只有成为这样的人才,我才能在职业生涯中不断前进并为企业的发展带来真正的价值。在这个不断变化的时代,我必须随时准备好迎接挑战,不断学习和探索新的领域,才能不断地向前推进。我坚信,只要我不断努力,我一定会达到自己的目标。