既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
String类型编码
我们最常使用的redis的一个数据类型就是String类型,实现单值缓存,分布式锁,计数器,分布式系统全局序列号等等功能。
它的底层编码分为三种,int,raw或者embstr。
int编码:存储整数值(例如:1,2,3),当 int 编码保存的值不再是整数值,又或者值的大小超过了long的范围,会自动转化成raw。例如:(1,2,3)->(a,b,c)
embstr编码:存储短字符串。
它只分配一次内存空间,redisObject和sds是连续的内存,查询效率会快很多,也正是因为redisObject和sds是连续在一起,伴随了一些缺点:当字符串增加的时候,它长度会增加,这个时候又需要重新分配内存,导致的结果就是整个redisObject和sds都需要重新分配空间,这样是会影响性能的,所以redis用embstr实现一次分配而后,只允许读,如果修改数据,那么它就会转成raw编码,不再用embstr编码了。
raw编码:用来存储长字符串。
它可以分配两次内存空间,一个是redisObject,一个是sds,二个内存空间不是连续的内存空间。和embstr编码相比,它创建的时候会多分配一次空间,删除时多释放一次空间。
版本区别:
embstr编码版本之间的区别:在redis3.2版本之前,用来存储39字节以内的数据,在这之后用来存储44字节以内的数据。
raw编码版本之间的区别:和embstr相反,redis3.2版本之前,可用来存储超过39字节的数据,3.2版本之后,它可以存储超过44字节的数据。
问题一:为什么是39字节?
从上面可以得知,embstr是一块连续的内存区域,由redisObject和sdshdr组成。
embstr最多占64字节场景:
redisObject占16个字节
struct RedisObject {
int4 type; // 4bits,不同的redis对象会有不同的数据类型(string、list、hash等),type记录类型,会用到4bits。
int4 encoding; // 4bits,存储编码形式,用4bits。
int24 lru; // 24bits,用24bits记录对象的LRU信息
int32 refcount; // 4bytes = 32bits,引用计数器,用到32bits
void *ptr; // 8bytes,64-bit system,指针指向对象的具体内容,需要64bits
}
计算: 4 + 4 + 24 + 32 + 64 = 128bits = 16bytes
sdshdr占48字节
struct sdshdr {
unsigned int len;//4个字节
unsigned int free;//4个字节
char buf[];//假设buf里面是39个字节
};
if (ptr) {
memcpy(sh->buf,ptr,len);
sh->buf[len] = ‘\0’;//一个字节
sdshdr的大小为8+39+1=48
那么一个embstr最多占64字节:16+48(4+4+1+39)=64
从2.4版本开始,redis用jemalloc内存分配器,比glibc的malloc要好一些,省内存,jemalloc会分配8,16,32,64等类型字节的内存。
embstr最小为33字节场景:
从上面我们可以得知redisObject占16个字节,现在buf中取8字节。
struct sdshdr {
unsigned int len;//4个字节
unsigned int free;//4个字节
char buf[];//假设buf里面是8个字节
};
if (ptr) {
memcpy(sh->buf,ptr,len);
sh->buf[len] = ‘\0’;//一个字节
sdshdr的大小为4+4+8+1=17
计算得出:16+17(4+4+1+8)=33
8,16,32都比33字节小,所以最小分配64字节。
通过对比:
16+17(4+4+1+8)=33
16+48(4+4+1+39)=64
当字符数大于8时,会分配64字节。当字符数小于39时,会分配64字节。这个默认39就是这样来的。
问题二:为什么分界值由39字节会变成44字节?
被暴打的回答是:REDIS_ENCODING_EMBSTR_SIZE_LIMIT值被换成了44了。
##define REDIS_ENCODING_EMBSTR_SIZE_LIMIT 39
##define REDIS_ENCODING_EMBSTR_SIZE_LIMIT 44
正经的回答是:
每个sds都有一个sdshdr,里面的len和free记录了这个sds的长度和空闲空间。
struct sdshdr {
unsigned int len;
unsigned int free;
用的unsigned int可以表示很大的范围,短的sds空间被浪费了(unsigned int len和unsigned int free 8个字节)
commit之后,unsigned int 变成了uint8_t,uint16_t,uint32_t
struct attribute ((packed)) sdshdr8 {
uint8_t len; /* used */
uint8_t alloc; /* excluding the header and null terminator */
char flags; /* 2 lsb of type, and 6 msb of refcount */
char buf[];
};
struct attribute ((packed)) sdshdr16 {
uint16_t len; /* used */
uint16_t alloc; /* excluding the header and null terminator */
char flags; /* 2 lsb of type, and 6 msb of refcount */
char buf[];
};
struct attribute ((packed)) sdshdr32 {
uint32_t len; /* used */
uint32_t alloc; /* excluding the header and null terminator */
char flags; /* 2 lsb of type, and 6 msb of refcount */
char buf[];
};
struct attribute ((packed)) sdshdr64 {
uint64_t len; /* used */
uint64_t alloc; /* excluding the header and null terminator */
char flags; /* 2 lsb of type, and 6 msb of refcount */
除此之外还将原来的sdshdr改成了sdshdr16,sdshdr32,sdshdr64
sizes = sdscatprintf(sizes,“sdshdr:%d”, (int)sizeof(struct sdshdr));
改成了
sizes = sdscatprintf(sizes,“sdshdr8:%d”, (int)sizeof(struct sdshdr8));
sizes = sdscatprintf(sizes,“sdshdr16:%d”, (int)sizeof(struct sdshdr16));
sizes = sdscatprintf(sizes,“sdshdr32:%d”, (int)sizeof(struct sdshdr32));
sizes = sdscatprintf(sizes,“sdshdr64:%d”, (int)sizeof(struct sdshdr64));
unsigned int占四个字节
uint8_t 占1个字节
Char 占一个字节
我们通过计算可以得出为什么优化之后会多出5个字节了,短字符串的embstr用最小的sdshdr8。
sdsdr8 = uint8_t * 2 + char = 1*2+1 = 3
sdshdr = unsigned int * 2 = 4 * 2 = 8
这么一算是不是少了五个字节了,所以3.2版本更新之后,由于优化小sds的内存使用,使得原本39个字节可以多使用5个字节,这就变成了44字节了。
问题三:Redis字符串最大长度是多少?
512M,查看源码可知。
static int checkStringLength(redisClient *c, long long size) {
if (size > 51210241024) {
addReplyError(c,“string exceeds maximum allowed size (512MB)”);
return REDIS_ERR;
}
return REDIS_OK;
}
List集合对象编码
List类型可以实现栈,队列,阻塞队列等数据结构,底层是个链表结构,它的底层编码分二种:ziplist(压缩列表) 和 linkedlist(双端链表)。
超过配置的数量或者最大的元素超过临界值时,符合配置的值,触发机制会选择不同的编码。
列表保存元素个数小于512个,每个元素长度小于64字节的时候触发机制会使用ziplist(压缩列表)编码,否则使用linkedlist(双端链表)。
在redis.conf(linux系统)或者redis.windows.conf(windows系统)对应的配置:
list-max-ziplist-entries 512
list-max-ziplist-value 64
通过修改配置这二个配置,设置触发条件选择编码。比如我修改列表保存元素个数小于1024个并且每个元素长度小于128字节时使用ziplist(压缩列表)编码,否则使用linkedlist(双端链表)。修改配置如下:
list-max-ziplist-entries 1024
list-max-ziplist-value 128
Hash对象编码
Hash类型比string类型消耗内存和cpu更小。Hash的编码有二种 ziplist编码 或者 hashtable。
超过指定的值,最大的元素超过临界值时,符合配置的值,触发机制选择不同的编码。列表保存元素个数小于512个,每个元素长度小于64字节的时候,使用ziplist(压缩列表)编码,否则使用hashtable 。
配置文件中可以通过修改set-max-intset-entries 1024达到改变列表保存元素个数小于1024个,原理类似。
hashtable 编码是字典作为底层实现,字典的键是字符串对象,值则全部设置为 null。在上面的字典也有详细介绍。
Set集合对象编码
Set类型可以实现抽奖小程序,点赞,收藏,加标签,关注模型等功能。Set的编码有二种intset 或者 hashtable。
超过指定的值,最大的元素超过临界值时,符合配置条件,触发机制选择不同的编码。集合对象中所有元素都是整数,对象元素数量不超过512时,使用intset编码,否则使用hashtable。原理大致和上面的类型相同。
列表保存元素个数的配置也是通过set-max-intset-entries进行修改的。
intset 编码用整数集合作为底层实现,hashtable编码可以类比HashMap的实现,HashTable类中存储的实际数据是Entry对象,数据结构与HashMap是相同的。
Zset有序集合对象编码
Zset适合做排序以及范围查询等功能,比如实现实现排行榜等。有序集合的编码有二种 ziplist 或者 skiplist。
保存的元素数量小于128,存储的所有元素长度小于64字节的时候,使用ziplist编码,否则用skiplist编码。修改配置如下:
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
ziplist 编码底层是用压缩列表实现的,集合元素是两个紧挨在一起的压缩列表节点来保存,第一个节点保存元素的成员,第二个节点保存元素的分值。 压缩列表的集合元素按照设置的分值从小到大的顺序进行排列,小的放置在靠近表头的位置,大的放置在靠近表尾的位置。
skiplist 编码的有序集合对象使用 zet 结构作为底层实现,一个 zset 结构同时包含一个字典和一个跳跃表。
字典的键保存元素的值,字典的值则保存元素的分值;跳跃表节点的 object 属性保存元素的成员,跳跃表节点的 score 属性保存元素的分值。这两种数据结构会通过指针来共享相同元素的成员和分值,所以不会产生重复成员和分值,造成内存的浪费。
问题:为什么需要二种数据结构?
有序集合单独使用字典或跳跃表,其中一种数据结构都可以实现,但是这里用两种数据结构组合起来。
原因是我们单独用字典,时间复杂度虽然低一些,查找成员的分值也快,但是字典以无序的方式来保存集合元素的,所以每次进行范围操作的时候都要重新进行排序,对性能有影响。
单独用跳跃表,虽然可以进行范围操作,但是查询复杂度变高了。因此Redis使用了两种数据结构来共同实现有序集合,可以保证排序的效率。
总结
以上就是今天要讲的内容,还希望各位读者大大能够在评论区积极参与讨论,给文章提出一些宝贵的意见或者建议📝,合理的内容,我会采纳更新博文,重新分享给大家。
🙏四连 关注🔎点赞👍收藏⭐️留言📝
感谢大家的支持,用心写博文分享给大家,你的支持(🔎点赞👍收藏⭐️留言📝)是对我创作的最大帮助。
🍊微信公众号:南北踏尘
🍊主页地址:java_wxid
🍊社区地址:幕后大佬
给读者大大的话
我本身是一个很普通的程序员,放在人堆里,除了与生俱来的🌹盛世美颜🌹、所剩不多的发量,就剩下180的大高个了。就是我这样的一个人,默默坚持写博文也有好多年了,有句老话说的好,🌕牛逼之前都是傻逼式的坚持🌕。希望自己可以通过大量的作品,时间的积累,个人魅力、运气和时机,可以打造属于自己的🌟技术影响力🌟。同时也希望自己可以成为一个🎄懂技术🎄,🎄懂业务🎄,🎄懂管理🎄的综合型人才,作为项目架构路线的总设计师,掌控全局的🌕团队大脑🌕,技术团队中的🍊绝对核心🍊是我未来几年不断前进的目标。
提示:以下都是资源分享,求个一键三连。
面试资料
福利大放送,🎉欢迎关注🔎点赞👍收藏⭐️留言📝,拜托了🙏,这对我真的很重要。
点击:面试资料
提取码:2021
200套PPT模板
福利大放送,🎉欢迎关注🔎点赞👍收藏⭐️留言📝,拜托了🙏,这对我真的很重要。
点击:200套PPT模板
提取码:2021
提问的智慧
福利大放送,🎉欢迎关注🔎点赞👍收藏⭐️留言📝,拜托了🙏,这对我真的很重要。
点击:提问的智慧
提取码:2021
Java开发学习路线
| 名称 | 链接 |
| — | — |
| JavaSE | 点击: JavaSE |
| MySQL专栏 | 点击: MySQL专栏 |
| JDBC专栏 | 点击: JDBC专栏 |
| MyBatis专栏 | 点击: MyBatis专栏 |
| Web专栏 | 点击: Web专栏 |
| Spring专栏 | 点击: Spring专栏 |
| SpringMVC专栏 | 点击: SpringMVC专栏 |
| SpringBoot专栏 | 点击: SpringBoot专栏 |
| SpringCould专栏 | 点击: SpringCould专栏 |
| Redis专栏 | 点击: Redis专栏 |
| Linux专栏 | 点击: Linux专栏 |
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
| SpringMVC专栏 | 点击: SpringMVC专栏 |
| SpringBoot专栏 | 点击: SpringBoot专栏 |
| SpringCould专栏 | 点击: SpringCould专栏 |
| Redis专栏 | 点击: Redis专栏 |
| Linux专栏 | 点击: Linux专栏 |
[外链图片转存中…(img-Y8Fr4wA9-1715282731638)]
[外链图片转存中…(img-5HJzYo8m-1715282731638)]
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!