面试必问 Redis 数据结构底层原理String、List,2024年最新mysql面试基础知识

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024b (备注Java)
img

正文

从上面分析可得Redis全局存储结构如下:

在这里插入图片描述

(这个图直接把我画裂开了,如有错误欢迎指正)

下面我们用"3w"方法来一一介绍下,每个数据类型,底层所用到了哪些数据结构(编码

)。

String 字符串

是什么

内部其实就是一个带长度信息的字节数组,原理类似Java中的ArrayList,可以动态扩容,所以很多特性都类似了,原理是相通的。内容是以二进制的形式存储的,所以 SDS(Simple Dynamic String) 可以存储任何类型的二进制数据,同时也不需要担心数据格式转换的问题。

struct SDS {

// …

T capacity; // 数组容量

T len; // 数组长度

byte[] content; // 数组内容

}

在这里插入图片描述

为什么

1.为什么申请空间比实际占用空间大,冗余了很多空位?

字符串支持append修改操作,如果没有冗余空间,那么追加操作必会引起频繁的数组扩容,而扩容是个耗时操作,所以通过空间预分配的方式来解决,即用冗余空间换时间。

2.实际使用长度len字段存在的意义是什么?

我们来用反证法证明,如果没有len来记录字符串长度,那么每次获取字符串长度时,就要调用默认的strlen函数来获取,而这个函数的时间复杂度是O(n),如果有了len,每次获取长度可以直接访问它,时间复杂度立马降至为O(1)。查询效率迎来质的飞跃,这块跟Arraylist的size原理一样。

如何实现

我们来直接用redis自带的debug命令看下实际存储对象的底层编码encoding,来看下底层使用了什么数据结构。

本文实例用的是redis版本:6.0.6

int编码

set key1 2000222222

OK

debug object key1

Value at:0x7f21f2eadd20 refcount:1 encoding:int serializedlength:5 lru:13142802 lru_seconds_idle:25

embstr编码

set key2 01234567890123456789012345678901234567890123 // 44个字符

OK

debug object key2

Value at:0x7f21f2e15140 refcount:1 encoding:embstr serializedlength:21 lru:13145749 lru_seconds_idle:5

raw编码

set key2 012345678901234567890123456789012345678901234 // 45个字符

OK

debug object key2

Value at:0x7f21f2eadd40 refcount:1 encoding:raw serializedlength:21 lru:13145765 lru_seconds_idle:2

总结:

为了节省内存空间,会按照实际存储字符串长度类型来选用不同编码

  • 存储的字符串可以转为long型,则用long类型存储,编码为int

  • 存储的字符串长度不大于44个字节时,用embstr编码

  • 存储的字符串长度大于44个字节时,用raw编码

编码类型分这么细的原因?

为了优先使用更紧凑的数据结构来解决问题,终极目标就是为了压缩内存、压缩内存、压缩内存。

raw和embstr的区别?

embstr编码: RedisObject的元数据,指针和SDS是连续的,可以避免内存碎片

raw编码: Redis会给SDS分配独立的空间,并用指针指向SDS结构

扩容策略

  • 字符串长度小于1MB时,采用加倍策略,ArrayList1.5

  • 字符串长度大于1MB时,采用每次扩容只加固定1MB

这个扩容策略,就比ArrayList高明了,当字符串比较大时,比如200M,每次还是double的话,400M,那就太浪费空间了,为了避免这种过大的空间浪费,使用了这种阈值判断方式,针对原始数据的不同大小采用相应的有效策略。

Reids规定了字符串最大长度不能超过512MB

使用场景

常用于缓存用户信息、原子加减。

注意: 原子计数是有范围的(long的范围),超过了会报错异常

List 链表

是什么
  • 版本3.2之前在Redis中使用的是压缩列表ziplist+双向链表linkedlist.

  • 版本3.2之后快速链表quickList

3.2之前初始化的 List 使用的压缩列表ziplist,随着数据增多,转化为双向链表linkedlist

压缩列表转化成双向链表的条件:

  • 如果添加的字符串元素长度超过默认值64

  • zip包含的节点数超过默认值512

这两个条件是可以修改的,在redis.conf中

list-max-ziplist-value 64

list-max-ziplist-entries 512

linkedlist

原理类似Java中的LinkedList,增删时间复杂度O(1),查询O(n).

typedef struct list{

//表头节点

listNode *head;

//表尾节点

listNode *tail;

//链表所包含的节点数量

unsigned long len;

// …

}

typedef struct listNode{

//前置节点

struct listNode *prev;

//后置节点

struct listNode *next;

//节点的值

void *value;

}

在这里插入图片描述

ziplist

ziplist是什么

ziplist压缩列表是内存地址连续,元素之间紧凑存储,功能类似链表的一种数据结构。

struct ziplist {

int32 zlbytes; // 整个列表占用字节数

int32 zltail_offset; // 达到尾部的偏移量

int16 zllength; // 存储元素实体个数

T[] entries; // 存储内容实体

int8 zlend; // 尾部标识

}

struct entry {

int prevlen; // 前一个entry的字节长度

int encoding;; // 元素类型编码

optional byte[] content; // 元素内容

}

在这里插入图片描述

为什么用ziplist?

因为普通的链表要附加prev、next前后指针浪费空间(64位操作系统每个指针占用8个字节),另外每个节点的内存是单独分配,会加剧内存的碎片化,影响内存管理效率。

如何实现

简单的来说就是用非指针连接的方式实现了双向链表的能力,能从头部和尾部(zltail)双向遍历没有维护双向指针prev next;而是存储上一个 entry的长度和 当前entry的长度,通过长度推算下一个元素在什么地方。牺牲读取的性能,获得高效的存储空间,因为(简短字符串的情况)存储指针比存储entry长度 更费内存。这是典型的“时间换空间”。只有字段、值比较小,才会用ziplist

优点:

  • 内存地址连续,省去了每个元素的头尾节点指针占用的内存,节省空间

缺点:

  • 插入数据、删除数据会导致连锁更新问题,有点儿类似Arraylist为保证内存连续性的数据移动的原理
quicklist

quicklist是什么

quickList是一个ziplist组成的双向链表。每个节点使用ziplist来保存数据。

为什么

为什么用quicklist

结合了 zipListlinkedList 的优点设计出来的,ziplist会引入频繁的内存申请和释放,而linkedlist由于指针也会造成内存的浪费,而且每个节点是单独存在的,会造成很多内存碎片,所以结合两个结构的特点,设计了quickList。

如何实现

debug看下encoding: quicklist

rpush key3 a b c

3

debug object key3

Value at:0x7f21f2eaddb0 refcount:1 encoding:quicklist serializedlength:22 lru:13150287 lru_seconds_idle:17 ql_nodes:1 ql_avg_node:3.00 ql_ziplist_max:-2 ql_compressed:0 ql_uncompressed_size:20

struct quicklist {

quicklistNode *head;

quicklistNode *tail;

long count; // 元素总数

// …

}

struct quicklistNode {

quicklistNode *prev;

quicklistNode *next;

ziplist *zl;// 压缩列表

最后

既已说到spring cloud alibaba,那对于整个微服务架构,如果想要进一步地向上提升自己,到底应该掌握哪些核心技能呢?

就个人而言,对于整个微服务架构,像RPC、Dubbo、Spring Boot、Spring Cloud Alibaba、Docker、kubernetes、Spring Cloud Netflix、Service Mesh等这些都是最最核心的知识,架构师必经之路!下图,是自绘的微服务架构路线体系大纲,如果有还不知道自己该掌握些啥技术的朋友,可根据小编手绘的大纲进行一个参考。

image

如果觉得图片不够清晰,也可来找小编分享原件的xmind文档!

且除此份微服务体系大纲外,我也有整理与其每个专题核心知识点对应的最强学习笔记:

  • 出神入化——SpringCloudAlibaba.pdf

  • SpringCloud微服务架构笔记(一).pdf

  • SpringCloud微服务架构笔记(二).pdf

  • SpringCloud微服务架构笔记(三).pdf

  • SpringCloud微服务架构笔记(四).pdf

  • Dubbo框架RPC实现原理.pdf

  • Dubbo最新全面深度解读.pdf

  • Spring Boot学习教程.pdf

  • SpringBoo核心宝典.pdf

  • 第一本Docker书-完整版.pdf

  • 使用SpringCloud和Docker实战微服务.pdf

  • K8S(kubernetes)学习指南.pdf

image

另外,如果不知道从何下手开始学习呢,小编这边也有对每个微服务的核心知识点手绘了其对应的知识架构体系大纲,不过全是导出的xmind文件,全部的源文件也都在此!

image

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
gBoo核心宝典.pdf

  • 第一本Docker书-完整版.pdf

  • 使用SpringCloud和Docker实战微服务.pdf

  • K8S(kubernetes)学习指南.pdf

[外链图片转存中…(img-g4fhhrSa-1713388694121)]

另外,如果不知道从何下手开始学习呢,小编这边也有对每个微服务的核心知识点手绘了其对应的知识架构体系大纲,不过全是导出的xmind文件,全部的源文件也都在此!

[外链图片转存中…(img-6N9FyLZl-1713388694121)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-veTv33aE-1713388694122)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值