MySQL底层原理(第一期)

一、执行一条 select 语句,期间发生了什么?

(整体的结构,有哪些模块,做什么,要理解,后面需要能讲出来)

连接器:建立连接,管理连接、校验用户身份;

查询缓存:查询语句如果命中查询缓存则直接返回,否则继续往下执行。MySQL 8.0 已删除该模块;

查询缓存:查询语句如果命中查询缓存则直接返回,否则继续往下执行。MySQL 8.0 已删除该模块;

执行 SQL:执行 SQL 共有三个阶段:

预处理阶段:检查表或字段是否存在;将 select * 中的 * 符号扩展为表上的所有列。

优化阶段:基于查询成本的考虑, 选择查询成本最小的执行计划;

执行阶段:根据执行计划执行 SQL 查询语句,从存储引擎读取记录,返回给客户端;

二、MySQL引擎篇:半道出家的InnoDB为何能替换官方的MyISAM?

(MySQL 存储引擎有哪些?Innodb 和 MyISAM 存储引擎有什么区别? )

存储方式MyISAM引擎会将表数据和索引数据分成两个文件存储。

索引支持 :因为MyISAM引擎的表数据和索引数据是分开的,因此不支持聚簇索引。

事务支持 :由于MyISAM引擎没有undo-log日志,所以不支持多条SQL组成事务并回滚。

故障恢复MyISAM引擎依靠bin-log日志实现,bin-log中未写入的数据会永久丢失。

锁粒度支持 :因为MyISAM不支持聚簇索引,因此无法实现行锁,所有并发操作只能加表锁。

并发性能MyISAM引擎仅支持表锁,所以多条线程出现读-写并发场景时会阻塞。

内存利用度MyISAM引擎过于依赖MySQL Server,对缓冲池、异步IO技术开发度不够。

三、MySQL 一行记录是怎么存储的?

(数据库表的数据存放在哪?InnoDB 表空间结构有哪些组成?varchar 是怎么保存长度的?Null 值是如何保存的?)

1.MySQL 的数据都是保存在磁盘的( SHOW VARIABLES LIKE 'datadir';)

2.表空间由段(segment)、区(extent)、页(page)、行(row)组成

3.InnoDB 行格式有哪些?

Redundant(没人用)、Compact(主流)、Dynamic(和Compact类似)和 Compressed(和Compact类似)

4.COMPACT 行格式长什么样?

一条完整的记录分为「记录的额外信息」和「记录的真实数据」两个部分。

5.varchar 是怎么保存长度的?

存到「变长字段长度列表」里面,读取数据的时候根据这个「变长字段长度列表」去读取对应长度的数据。

6.Null 值是如何保存的?

MySQL 的 Compact 行格式中会用「NULL值列表」来标记值为 NULL 的列,NULL 值并不会存储在行格式中的真实数据部分。

NULL值列表会占用 1 字节空间,当表中所有字段都定义成 NOT NULL,行格式中就不会有 NULL值列表,这样可节省 1 字节的空间。

四、char和varcahr的区别

1.varchar和char在MySQL层的区别

varchar变长 的(Variable-length),char定长的(Fixed-length)。

最大长度:char是255,varchar是65535,单位是字符(而不是字节)。

尾随空格 :char会将尾随空格去掉,而varchar不会。 ​ 因为存储时,char会用空格填充至指定长度,所以取出时需要去除空格。如果char字段有唯一索引,aa会提示唯一索引冲突。

存储空间占用:varchar会占用额外的1~2字节来存储字符串长度。如果最大长度超过255,就需要2字节,否则1字节。

2.varchar和char在存储引擎层的区别

varchar类型对于短字符串、长字符串、多字节编码,都是存储了实际的字符+字符长度。

char类型 的字段,innodb同样保存了字符长度(红色字体)。对于utf8mb4 char(50)来说,长度不够50字节的字符串,会使用空格(0x20)填充到50个字节

3.char和varchar存储对比

char和varchar都会存储字符串长度

对于CHAR(N)字段,如果实际存储数据小于N字节,会填充空格到N个字节。

4.性能对比

char填充空格可能导致浪费存储空间,进而导致性能下降。

极端场景 :某个字段的最大长度是100字节,但是会频繁修改。如果使用char(100),则插入记录后就分配了100个字节,后续修改不会造成页分裂、页空隙等问题,而varchar(100)由于没有提前分配存储空间,后续修改时可能出现页分裂,进而导致性能下降。

5.char和varchar 使用场景

char 适应于固定长度的码值、定值;更新次数多的小长度字段;

varchar 适应于不定的字段,如审核意见、理由;更新次数少的字段;系统性能需要;

五、从数据页的角度看 B+ 树

(聚簇索引和非聚簇索引(二级索引)的 B+ 树结构有什么区别?)

InnoDB 的数据是按「数据页」为单位来读写的,默认数据页大小为 16 KB。每个数据页之间通过多个链表的形式组织起来,物理上不连续,但逻辑上连续。

数据页内包含用户记录,每个记录之间用单向链表的方式组织高效起来,为了加快在数据页内查询记录的速度,设计了一个页目录,页目录存储各个槽(分组),且主键值是分组的,那么可以通过二分查找法的方式进行检索从而提高效率。

为了高效查询记录每个所在的数据页,InnoDB采用b+树作为索引,节点都是一个数据页。

如果叶子节点存储的实际数据 的就是聚簇索引 ,一个表中只能有一个聚簇索引;如果叶子节点存储的不是实际数据,而是主键值 ,则就是二级索引,一个表中可以有多个二级索引。

在使用二级索引进行查找数据时,如果查询的数据能在二级索引找到,那么就是「索引覆盖」操作,如果查询的数据不在二级索引里,就需要先在二级索引找到主键值,需要去聚簇索引中获得数据行,这个过程就叫作「回表」。

六、揭开 Buffer Pool 的面纱

(InnoDB 对 LRU 做了哪些优化?)

1.Innodb 存储引擎设计了一个缓冲池(Buffer Pool),来提高数据库的读写性能。

2.Buffer Pool 以页为单位缓冲数据,可以通过 innodb_buffer_pool_size 参数调整缓冲池的大小,默认是 128 M。

3.Innodb 通过三种链表来管理缓页:

Free List (空闲页链表),管理空闲页;

Flush List (脏页链表),管理脏页;

LRU List,管理脏页+干净页,将最近且经常查询的数据缓存在其中,而不常查询的数据就淘汰出去。;

4.InnoDB 对 LRU 做了一些优化,我们熟悉的 LRU 算法通常是将最近查询的数据放到 LRU 链表的头部,而 InnoDB 做 2 点优化:

将 LRU 链表 分为young 和 old 两个区域,加入缓冲池的页,优先插入 old 区域;页被访问时,才进入 young 区域,目的是为了解决预读失效的问题。

当**「页被访问」且「 old 区域停留时间超过 innodb_old_blocks_time 阈值(默认为1秒)」**时,才会将页插入到 young 区域,否则还是插入到 old 区域,目的是为了解决批量数据访问,大量热数据淘汰的问题。

5.可以通过调整 innodb_old_blocks_pct 参数,设置 young 区域和 old 区域比例。

6.在开启了慢 SQL 监控后,如果你发现「偶尔」会出现一些用时稍长的 SQL,这可因为脏页在刷新到磁盘时导致数据库性能抖动。如果在很短的时间出现这种现象,就需要调大 Buffer Pool 空间或 redo log 日志的大小。

七、普通索引和唯一索引,应该怎么选择?

(普通索引和唯一索引有什么区别? 哪个更新性能更好?)

查询过程:性能差距微乎其微

普通索引,查找到满足条件的第一个记录后,需要查找下一个记录,直到碰到第一个不满足 k=5 条件的记录。

唯一索引,由于索引定义了唯一性,查找到第一个满足条件的记录后,就会停止继续检索。

更新过程:change buffer 减少了随机磁盘访问,更新性能提升明显。

唯一索引 的更新就不能使用 change buffer,实际上也只有普通索引可以使用。

索引选择和实践

这两类索引在查询能力上是没差别 的,主要考虑的是对更新性能 的影响。所以,建议尽量选择普通索引

如果所有更新后面,都马上伴随着对这个记录的查询,那么应该关闭 change buffer。而在其他情况下,change buffer 都能提升更新性能。

八、索引:排序的艺术

(什么是索引?为什么索引能加快查询?索引的数据结构是什么?)

1.索引是什么?

索引是提升查询速度的一种数据结构 ,索引之所以能提升查询速度,在于它在插入时对数据进行了排序(它的缺点是影响插入或者更新的性能)InnoDB 存储引擎支持的索引有 B+ 树索引、全文索引、R 树索引

2.B+树索引结构

B+树索引结构是目前为止排序最有效率的数据结构

B+树索引的特点是: 基于磁盘的平衡树,但树非常矮,通常为 34 层,能存放千万到上亿的排序数据。树矮意味着访问效率高,从千万或上亿数据里查询一条数据,只用 3、4 次 I/O。又因为现在的固态硬盘每秒能执行至少 10000 次 I/O ,所以查询一条数据,哪怕全部在磁盘上,也只需要 0.003 ~ 0.004 秒。另外,因为 B+ 树矮,在做排序时,也只需要比较 34 次就能定位数据需要插入的位置,排序效率非常不错。

B+ 树索引由根节点(root node)、中间节点(non leaf node)、叶子节点(leaf node)组成 ,其中叶子节点存放所有排序后的数据。 当然也存在一种比较特殊的情况,比如高度为 1 的B+ 树索引

九、为什么 MySQL 采用 B+ 树作为索引

(B+ 树和(B 树和红黑树)有什么区别?为什么选择用 B+ 树 作为索引数据结构?)

1.怎样的索引的数据结构是好的?

能在尽可能少的磁盘的 I/O 操作中完成查询工作;

要能高效地查询某一个记录,也要能高效地执行范围查找;

2.什么是二分查找?

假设我们现在用数组来存储索引,比如下面有一个排序的数组,如果要从中找出数字 3,最简单办法就是从头依次遍历查询,这种方法的时间复杂度是 O(n),查询效率并不高。因为该数组是有序的,所以我们可以采用二分查找法,比如下面这张采用二分法的查询过程图:

可以看到,二分查找法每次都把查询的范围减半,这样时间复杂度就降到了 O(logn),但是每次查找都需要不断计算中间位置。

3.什么是二分查找树(不适合做索引结构)

用数组来实现线性排序的数据虽然简单好用,但是插入新元素的时候性能太低。

因为插入一个元素,需要将这个元素之后的所有元素后移一位,如果这个操作发生在磁盘中呢?这必然是灾难性的。因为磁盘的速度比内存慢几十万倍,所以我们不能用一种线性结构将磁盘排序。

其次,有序的数组在使用二分查找的时候,每次查找都要不断计算中间的位置。

二叉查找树: 找到所有二分查找中用到的所有中间节点把他们用指针连起来,并将最中间的节点作为根节点

二叉查找树的特点是一个节点的左子树的所有节点都小于这个节点,右子树的所有节点都大于这个节点,这样我们在查询数据时,不需要计算中间节点的位置了,只需将查找的数据与节点的数据进行比较。

假设,我们查找索引值为 key 的节点:

如果 key 大于根节点,则在右子树中进行查找;

如果 key 小于根节点,则在左子树中进行查找;

如果 key 等于根节点,也就是找到了这个节点,返回根节点即可。

二叉查找树解决了插入新节点的问题 ,因为二叉查找树是一个跳跃结构,不必连续排列。这样在插入的时候,新节点可以放在任何位置,不会像线性结构那样插入一个元素,所有元素都需要向后排列。

二叉查找树解决了连续结构插入新元素开销很大的问题,同时又保持着天然的二分结构。

二叉查找树存在一个极端情况: 当每次插入的元素都是二叉查找树中最大的元素,二叉查找树就会退化成了一条链表,查找数据的时间复杂度变成了 O(n)

由于树是存储在磁盘中的,访问每个节点,都对应一次磁盘 I/O 操作(假设一个节点的大小「小于」操作系统的最小读写单位块的大小),也就是说树的高度就等于每次查询数据时磁盘 IO 操作的次数,所以树的高度越高,就会影响查询性能。

二叉查找树由于存在退化成链表的可能性,会使得查询操作的时间复杂度从 O(logn) 升为 O(n)。

而且会随着插入的元素越多,树的高度也变高,意味着需要磁盘 IO 操作的次数就越多,这样导致查询性能严重下降,再加上不能范围查询,所以不适合作为数据库的索引结构。

4.什么是自平衡二叉树?

在二叉查找树的基础上增加了一些条件约束:每个节点的左子树和右子树的高度差不能超过 1。也就是说节点的左子树和右子树仍然为平衡二叉树,这样查询操作的时间复杂度就会一直维持在 O(logn) 。

除了平衡二叉查找树,还有很多自平衡的二叉树,比如红黑树,它也是通过一些约束条件来达到自平衡。

不管平衡二叉查找树还是红黑树,都会随着插入的元素增多,而导致树的高度变高,这就意味着磁盘 I/O 操作次数多,会影响整体数据查询的效率。

当树的节点越多的时候,并且树的分叉数 M 越大的时候,M 叉树的高度会远小于二叉树的高度

5.什么是 B 树

为了解决降低树的高度的问题,后面就出来了 B 树,它不再限制一个节点就只能有 2 个子节点,而是允许 M 个子节点 (M>2),从而降低树的高度。

B 树的每一个节点最多可以包括 M 个子节点,M 称为 B 树的阶,所以 B 树就是一个多叉树。

假设 M = 3,那么就是一棵 3 阶的 B 树,特点就是每个节点最多有 2 个(M-1个)数据最多有 3 个(M个)子节点 ,超过这些要求的话,就会分裂节点。

假设我们在上图一棵 3 阶的 B 树中要查找的索引值是 9 的记录那么步骤可以分为以下几步:

与根节点的索引(4,8)进行比较,9 大于 8,那么往右边的子节点走;

然后该子节点的索引为(10,12),因为 9 小于 10,所以会往该节点的左边子节点走;

走到索引为9的节点,然后我们找到了索引值 9 的节点。

可以看到,一棵 3 阶的 B 树在查询叶子节点中的数据时,由于树的高度是 3 ,所以在查询过程中会发生 3 次磁盘 I/O 操作。

B 树的每个节点都包含数据(索引+记录),而用户的记录数据的大小很有可能远远超过了索引数据,这就需要花费更多的磁盘 I/O 操作次数来读到「有用的索引数据」。

在我们查询位于底层的某个节点(比如 A 记录)过程中,「非 A 记录节点」里的记录数据会从磁盘加载到内存,但是这些记录数据是没用的,我们只是想读取这些节点的索引数据来做比较查询,而「非 A 记录节点」里的记录数据对我们是没用的,这样不仅增多磁盘 I/O 操作次数 ,也占用内存资源

如果使用 B 树来做范围查询的话,需要使用中序遍历,这会涉及多个节点的磁盘 I/O 问题,从而导致整体速度下降。

6.什么是 B+ 树?

B+ 树就是对 B 树做了一个升级,MySQL 中索引的数据结构就是采用了 B+ 树

B+ 树与 B 树差异的点,主要是以下这几点:

叶子节点(最底部的节点)才会存放实际数据(索引+记录),非叶子节点只会存放索引;

所有索引都会在叶子节点出现,叶子节点之间构成一个有序链表;

非叶子节点的索引也会同时存在在子节点中,并且是在子节点中所有索引的最大(或最小)。

非叶子节点中有多少个子节点,就有多少个索引;

下面通过三个方面,比较下 B+ 和 B 树的性能区别。

单点查询

B 树进行单个索引查询时,最快可以在 O(1) 的时间代价内就查到,而从平均时间代价来看,会比 B+ 树稍快一些。

但是 B 树的查询波动会比较大,因为每个节点即存索引又存记录,所以有时候访问到了非叶子节点就可以找到索引,而有时需要访问到叶子节点才能找到索引。

B+ 树的非叶子节点不存放实际的记录数据,仅存放索引,因此数据量相同的情况下,相比存储即存索引又存记录的 B 树,B+树的非叶子节点可以存放更多的索引,因此 B+ 树可以比 B 树更「矮胖」,查询底层节点的磁盘 I/O次数会更少。

插入和删除效率

B+ 树有大量的冗余节点,这样使得删除一个节点的时候,可以直接从叶子节点中删除,甚至可以不动非叶子节点,这样删除非常快 ,因此,B+ 树的插入和删除效率更高

范围查询

因为 B+ 树所有叶子节点间还有一个链表进行连接,这种设计对范围查找非常有帮助

因此,存在大量范围检索的场景,适合使用 B+树,比如数据库。而对于大量的单个索引查询的场景,可以考虑 B 树,比如 nosql 的MongoDB。

7.MySQL 中的 B+ 树

Innodb 存储引擎,是采用了 B+ 树作为了索引的数据结构。

但是 Innodb 使用的 B+ 树有一些特别的点,比如:

B+ 树的叶子节点之间是用「双向链表」进行连接,这样的好处是既能向右遍历,也能向左遍历。

B+ 树节点内容是数据页,数据页里存放了用户的记录以及各种信息,每个数据页默认大小是 16 KB。

Innodb 根据索引类型不同 ,分为聚簇二级索引。他们区别在于:

因为表的数据都是存放在聚簇索引的叶子节点里,所以 InnoDB 存储引擎一定会为表创建一个聚簇索引,且由于数据在物理上只会保存一份,所以聚簇索引只能有一个,而二级索引可以创建多个。

相关推荐
QX_hao5 小时前
【Go】--map和struct数据类型
开发语言·后端·golang
MC丶科6 小时前
【SpringBoot 快速上手实战系列】5 分钟用 Spring Boot 搭建一个用户管理系统(含前后端分离)!新手也能一次跑通!
java·vue.js·spring boot·后端
G探险者6 小时前
为何一个系统上线要经过N轮测试?带你看懂企业级发布体系
后端
TDengine (老段)7 小时前
TDengine 数学函数 DEGRESS 用户手册
大数据·数据库·sql·物联网·时序数据库·iot·tdengine
TDengine (老段)7 小时前
TDengine 数学函数 GREATEST 用户手册
大数据·数据库·物联网·时序数据库·iot·tdengine·涛思数据
@yanyu6667 小时前
idea中配置tomcat
java·mysql·tomcat
安当加密8 小时前
云原生时代的数据库字段加密:在微服务与 Kubernetes 中实现合规与敏捷的统一
数据库·微服务·云原生
lang201509288 小时前
Spring Boot 入门:5分钟搭建Hello World
java·spring boot·后端
爱喝白开水a8 小时前
LangChain 基础系列之 Prompt 工程详解:从设计原理到实战模板_langchain prompt
开发语言·数据库·人工智能·python·langchain·prompt·知识图谱