MySQL_01_MySQL架构和InnoDB架构

一、前言

MySQL刚开始的是存储引擎是ISAM,然后是MyISAM,现在的InnoDB存储引擎是第三方开始,只是后来mysql和innodb都被oracle收购,最后创始人开发了mariadb。

用一条select语句来学习Mysql架构(连接层-服务层-存储引擎层),用一条update语句(where都磁盘,update写磁盘)来学习Innodb架构,是最好的方式。

二、MySQL架构和InnoDB架构

对于 MySQL,一张是 MySQL 架构图,另一张则是 InnoDB 架构图:

在这里插入图片描述

从上面第一张图可以看到,MySQL主要分为三大块:连接层、服务层、存储引擎层。

在这里插入图片描述

从上面第二张图可以看到,InnoDB 主要分为两大块:

(1) InnoDB In-Memory Structures,即InnoDB 内存架构,在上图左边,包括Buffer Pool、Change Buffer、Adaptive Hash Index、Log Buffer;

(2) InnoDB On-Disk Structures,即InnoDB 磁盘架构,在上图右边,五个表空间(The System Tablespace(如undo log、Change Buffer,以及 Doublewrite Buffer )、File-Per-Table Tablespaces、General Tablespace、Undo Tablespaces、Temporary Tablespaces)和 redo log 文件。

第一张图和第二张图的联系:第一张图最底下的是存储引擎层(Storage Engines),MySQL默认的存储引擎是InnoDB,它决定了 MySQL 会怎样存储数据,怎样读取和写入数据,也在很大程度上决定了 MySQL 的读写性能和数据可靠性。

对于这么重要的一层能力,MySQL 提供了极强的扩展性,你可以定义自己要使用什么样的存储引擎:InnoDB、MyISAM、MEMORY、CSV,甚至可以自己开发一个存储引擎然后使用它。常见的,InnoDB支持事务,MyISAM不支持事务。

三、MySQL 架构图

我们从一条select查询SQL语句出发,看一下MySQL的整个架构,如下:
在这里插入图片描述
五个:连接器、缓存、分析器、优化器、执行器

三个层:连接层、服务层(优化SQL,对存储引擎返回的数据过滤、计算)、存储引擎(仅管理数据)

在这里插入图片描述

3.1 连接器:从服务端jdbc到底层mysql

mysql连接器(比如Navicat dbeaver这样图像化界面就可以连接上mysql,cmd命令也可以连接上mysql)

3.1.1 连接器三要素:通信类型、连接方式、协议

对于mysql,监听的端口是3306,client连接server有多种方式,连接三要素:通信类型、连接方式、协议。

(1)client连接server,对于 通信类型 来说:有同步通信和异步通信两种方式,使用哪种方式取决于client的连接怎么写,一般是直接写四要素就是同步,异步通信写起来比较复杂,还会造成数据的混淆。

(2)client连接server,对于 连接方式 来说:有长连接和短连接两种方式。
长连接:一个client用完,连接还在,其他的client还可以用,我们一般使用长连接。
短连接:一个client用完,连接断开,其他的client需要重新建立连接。

(3)client连接server,对于 协议 来说:有 TCP 和 Unix Socket 两种方式。
本机client连接本机server,使用 Unix Socket;其他client连接本机server,使用 TCP。

mysql 的很多参数的设置包括 session和 global两个级别。
mysql是单进程多线程的模型,client一个session连接就是server一个线程,默认最大线程 151 最大可用设置为 10 0000 (10万)。

3.1.2 连接器的过程

我们要查询mysql,第一步就是先去连接mysql,那这个时候就是mysql连接器跟我们对接。他负责跟客户端建立链接、获取权限、维持和管理连接。链接的时候会经过TCP(协议)握手,然后身份验证,然后我们输入用户名密码就好了。验证ok后,我们就连上了这个MySQL服务了,但是这个时候我们处于空闲状态。

mysql连接使用的是 TCP/IP 协议。

对于MySQL连接器,怎么查看mysql空闲连接列表?
回答:一条命名就够了show processlist

执行:
在这里插入图片描述
其中的Command列显示为Sleep的这一行,就表示现在系统里面有一个空闲连接。这里需要注意的是,我们数据库的客户端太久没响应,mysql连接器就会自动断开了,这个时间参数是wait_timeout控制住的,默认时长为8小时。如果mysql连接器已经断开了,但是程序员继续重连的时候会报错,如果想再继续操作数mysql,你就需要重连了。

问题:对于MySQL连接器,每天早上都得重启一下应用程序,否则就提示连接数据库失败,他们都不知道该怎么办?
回答:按照这个错误提示,应该就是连接时间过长了,断开了连接。数据库默认的超时时间是8小时,而他们平时六点下班,下班之后系统就没有人用了,等到第二天早上九点甚至十点才上班,这中间的时间已经超过10个小时了,数据库的连接肯定就会断开了。是的,就是超出了超时时间,然后写代码的人也没注意到这个细节,所以才会出现这个问题。把超时时间改得长一点,问题就解决了。

问题:那除了重新链接,还有别的方式么?因为建立链接还是比较麻烦的。
回答:使用长连接。
但是这里有个缺点,使用长连接之后,内存会飙得很快,我们知道MySQL在执行过程中临时使用的内存是管理在连接对象里面的,只有在链接断开的时候才能得到释放,那如果一直使用长连接,那就会导致OOM(Out Of Memory),会导致MySQL重启,在JVM里面就会导致频繁的Full GC。

问题:如何解决长连接占用内存大?
方法一:我一般会定期断开长连接,使用一段时间后,或者程序里面判断执行过一个占用内存比较大的查询后就断开连接,需要的时候重连就好了。
方法二:执行比较大的一个查询后,执行mysql_reset_connection可以重新初始化连接资源。这个过程相比第一种方式会好点,不需要重连,但是会初始化连接的状态。

3.2 mysql缓存

MySQL自带的查询缓存执行流程?mysql缓存的优点?

MySQL拿到一个查询请求后,会先到查询缓存看看,之前是不是执行过这条语句。如果日常开发中,在不使用redis的情况下,同一条语句在MySQL执行两次,第一次和后面的时间是不一样的,后者明显快一些,这就是因为mysql缓存的存在。mysql缓存跟Redis一样,只要是你之前执行过的语句,都会在内存里面用key-value形式存储着。查询的时候就会拿着语句先去缓存中查询,如果能够命中就返回缓存的value,如果不命中就执行后面的阶段。

mysql缓存的缺点?为什么要引入redis作为缓存?

mysql的缓存很容易失效,设置上,只要对表有任何的更新,这个表的所有查询缓存就会全部被清空,就会出现缓存还没使用,就直接被清空了,或者积累了很多缓存准备用来着,但是一个更新打回原形。这就导致查询的命中率低的可怕,只有那种只查询不更新的表适用缓存,但是这样的表往往很少存在,一般都是什么配置表之类的。所以说,mysql是一个很好的轻量级数据库,但是缓存方面做的不好,所以,开发中,mysql数据库+redis缓存配置使用。

使用层面,设置禁用mysql缓存?设置启用mysql缓存?

禁用mysql缓存:query_cache_type设置成为DEMAND;
启用mysql缓存:query_cache_type设置成为SQL_CACHE。
禁用mysql缓存2(sql语句层面):select SQL_NO_CACHE * from B

一般来说,sql语句默认执行是有缓存的,在sql前面使用SQL_NO_CACHE不使用缓存。既然用缓存比不用缓存快一点点,那么SQL_NO_CACHE意义是什么?SQL_NO_CACHE的意义:知道真正的查询时间。

mysql缓存不好用,一个鸡肋功能,用redis做缓存深入人心,所以mysql缓存在MySQL5.7之后就默认关闭了,在MySQL8.0之后就取消了。

mysql缓存在MySQL5.7之后就默认关闭了,在MySQL8.0之后就取消了。

3.3 分析器/解析器parser(词法分析+语法分析)

分析器-优化器-执行器触发:在缓存没有命中的情况下,就开始执行语句了(缓存命中就直接从缓存中拿SQL语句),即 分析器+优化器+执行器。对于一条语句来说,缓存不是必须,但是 分析器+优化器+执行器 整个过程一定要完成。

分析器定义:又称解析器parser,检查sql语句是否有错误,包括 词法分析 + 语法分析。

词法分析定义:将SQL语句进行拆分,即把一个完整的SQL语句打碎成一个个的单词,检查SQL语句中的关键字、表名、列名。

语法分析定义:根据词法分析的结果进行语法检查,语法分析会判断你sql的对错,错了会提醒你的,并且会提示你哪里错了。

经过 词法分析和语法分析 ,会得到一个这个的解析树,如下:

将一个SQL语句 select name from user_info where id =1 and age>20 进行词法分析和
在这里插入图片描述

其实,在词法分析和语法分析之后,还有一个预处理器pre-processor,进行 语义分析。比如:表名是否存在,别名错了,列名是否存在,就是将当前SQL结合具体的数据库的数据,进行分析(上面的词法分析和语法分析,仅仅是针对 sql 本身检查,不结合数据)。

预处理器pre-processor的结果还是一个解析树,但是是预处理器pre-processor之后得到的解析树,和语法解析之后的解析树可能会有不同。

经过解析器parser,sql语句就变成了 解析树 数据结构。

3.4 优化器optimizer(索引优化+多表关联查询优化)

3.4.1 优化器选择成本最低的执行路径

一条sql语句可以有多种执行方法,mysql会进行成本分析,优化之后执行,这就是优化器的用武之地。一个SQL语句在mysql中有多个执行路径(又称执行计划,即sql语句具体如何执行),这个是一个基于cost的优化。

优化器定义:操作的表可能会建立很多索引,优化有一步就是要确认使用哪个索引,比如使用你的主键索引,联合索引还是什么索引更好。还有就是对执行顺序进行优化,条件那么多,先查哪个表,还是先关联,会出现很多方案,最后由优化器决定选用哪种方案。

经过优化器,sql语句就从之前的 解析树 数据结构,变成了一个 执行计划 数据结构,这样就可以交给 执行器 一个非常清晰的、具体的执行任务了。

3.4.2 程序员需要知道两个技巧 explain optimizer_trace

我们可以通过 explain 命令查看这个优化器
explain format=json

在这里插入图片描述

optimizer_trace 分为三个阶段:准备阶段、优化阶段、执行阶段。
在这里插入图片描述

3.5 执行器

执行器定义:优化器得到的结果,交给执行器执行。

第一步:权限的判断,MySQL默认权限开放。

第二步:一行一行的去判断是否满足条件,有索引的执行起来可能就好点,一行行的判断就像是接口都提前在引擎定义好了,所以他比较快。数据库的慢日志有个rows_examined字段,扫描多少行可以看到,还有explain也可以看到执行计划,我们扫描了多少行。

注意:慢查询就是执行器里面的.

在这里插入图片描述

执行器接收到执行计划,调用存储引擎的接口拿到数据(存储引擎再到文件管理系统(内存和磁盘)里面拿到数据),将数据放一份到query cache里面,然后返回给客户端。

3.6 从MyISAM到InnoDB

3.6.1 存储引擎的磁盘文件支持在哪里

对于mysql这种关系型数据库,数据放在表结构里面,但是有不同的表类型,innodb memory myisam

一般在 C:\ProgramData\Data\MySQL Server 5.7\Data\数据库名
innodb的

在这里插入图片描述

3.6.2 存储引擎在建表的时候确定

表类型怎么来的,就是在create table中指定的,如下:

在这里插入图片描述
如果不指定,MySQL5.5 之后,默认存储引擎就是innodb。MySQL5.5 之后,默认存储引擎就是Myisam。

3.6.3 存储引擎可以修改

存储引擎表示表里面数据的不同的存储方式,是否可以修改?可以修改
比如,建一个表,执行存储引擎为myisam,然后插入 100万 条数据,插完之后修改为 innodb 表类型。

3.6.4 表功能决定存储引擎的选用

在这里插入图片描述

四、InnoDB架构

在这里插入图片描述

从上面第二张图可以看到,InnoDB 主要分为两大块:
InnoDB In-Memory Structures InnoDB 内存架构 上图左边,一个包括四个: Buffer Pool、Change Buffer、Adaptive Hash Index、Log Buffer
InnoDB On-Disk Structures InnoDB 磁盘架构 上图右边,一共包括六个:五个表空间(The System Tablespace(undo log、Data Dictionary、Change Buffer、 Doublewrite Buffer )、General Tablespace、File-Per-Table Tablespace、Temporary Tablespace、Undo Tablespace)+ redo log

4.1 InnoDB 架构第一部分,InnoDB 内存架构

内存部分记住四个:
buffer pool 写盘 insert update delete,没有select,因为select只要读盘,不写盘
change buffer 读盘 select update delete,没有insert,因为insert只要写盘,不读盘
log buffer,redolog是顺序io,本身是为了保证断电bufferpool中的数据的不丢失,但是redolog也分为内存部分的logbuffer和磁盘部分的redolog,每秒同步,最多丢失一秒数据。
adaptive hash index,自适应哈希索引,哈希索引两个缺点,哈希冲突和无法范围查找,有一个优点,就是速度快。

4.1.1 Buffer Pool

4.1.1.1 高效写盘且数据无断电丢失风险

前言:innodb写磁盘,在 buffer pool + redo log 的辅助下,既保证读写磁盘效率,也保证bufferpool的断电风险兜底。

buffer pool是内存中一个区域,innodb缓冲表和索引被存放在这里。正如之前提到的,MySQL 不会直接去修改磁盘的数据,因为这样做太慢了。InnoDB的数据放在磁盘里,不是用户需要读取10个字节就读取10个字节,而且是和操作系统一样,读取若干个page,这是一种预读取的设计,使用的是程序的局部性原理。

问题:buffer pool在运行中如何使用?
回答:bufferpool为批量写磁盘提供保证。对于用户的添加、修改、删除的sql语句的执行,MySQL 会先修改内存中的数据,具体点,修改内存中的 Buffer Pool 区域中的数据,然后每一次修改行记录就写到 redo log 日志文件中,等有空了再刷磁盘(磁盘读取redo log日志文件,更新磁盘上的数据库)。

操作系统,磁盘读取单位为page数据页,默认大小为4KB
InnoDB ,磁盘读取单位为page数据页,默认大小为16KB,可以设为4KB 8KB 16KB 32KB 64KB
在这里插入图片描述

innodb 每次都是从磁盘读取16KB的数据,到innodb的内存,读磁盘先到buffer pool里面找,要写磁盘先写到buffer pool,但是这个buffer pool属于内存部分,断电丢失,buffer pool没有同步写磁盘,属于脏页,后台有一个刷脏线程,但是是间隔的。

内存缓冲区buffer pool + redo log(保证事务持久性,属于磁盘部分,断电不丢失)
buffer pool为了保证高效
redo log为了保证断电不丢失数据

在这里插入图片描述

上图表示,对于断电,buffer pool中尚未刷盘的脏页数据丢失了,但是用户确实是commit这些事务的,没关系,我们在磁盘上有redolog,再次启动的时候,马上将redolog数据写到dbfile中去。

问题:写数据到磁盘从 数据(内存) --> dbfile(磁盘) 变为 数据(内存) --> buffer pool(内存) --> redo log(磁盘) --> dbfile(磁盘) 是不是变复杂了?加了redo log会不会变慢
回答:写redolog是顺序io,比直接写dbfile 随机io 快的多。

通过buffer pool提高了读写效率之后,再加一个redo log保证断电commit持久性,效率依然不会受到影响,因为虽然这个redo log在磁盘上(保证断电不丢失),但是写这个redolog是顺序io,非常快,效率依然不会受到影响。

redo log大小固定的,默认是 48M 大小,写满了,buffer pool,redo log就不能为bufferpool的断电风险兜底,bufferpool必须刷盘。
在这里插入图片描述
redolog和undolog如何在磁盘中存储?
redo log(磁盘部分)

在这里插入图片描述

undolog的文件存储里面有两个独立的文件,是存放在mysql根目录下,就是 ib_logfile0 ib_logfile1
在这里插入图片描述

undo log(也是磁盘部分)
记录事务发生之前的数据状态(redolog是记录事务发生之后的数据状态),发生异常时回滚,保证原子性

undolog默认存储路径是mysql安装的根路径
在这里插入图片描述
undolog并没有一个独立的文件(redolog有两个独立文件),是存放在一个系统表空间里面的,也就是ibdata1

在这里插入图片描述

4.1.1.2 buffer pool内部的双向链表结构

我们平时开发时,会用 redis 来做缓存,缓解数据库压力,其实 MySQL 自己也做了一层类似缓存的东西。MySQL 是以「页」(page)为单位从磁盘读取数据的,Buffer Pool 里的数据也是如此,实际上,Buffer Pool 是a linked list of pages,一个以页为元素的链表LRU。

金手指1:MySQL底层,B+树索引的一个节点大小就是一个页大小;
金手指2:buffer pool虽然被称为缓冲池,本质是一个以页为元素的链表。

关于磁盘上的buffer pool文件,我们可以直接查看如下信息:

在这里插入图片描述
buffer pool使用的是内存的空间,最大可用使用内存的80%,但是总是有使用完的一天,怎么办?
这就是LRU算法

(1) LRU算法维护一个双向链表,节点里面存放的并不是数据记录(数据行),而是指向数据记录的指针;
(2) buffer pool LRU 使用冷热分离,有点类似JVM的年轻代、老年代,热数据区new subList 占用 5/8,冷数据区new subList 占用 5/8,两个数据区域都设置head指针和tail指针,一个新数据记录的指针进来,先放到冷数据区,由head指向,如果一直不没有被访问,就会慢慢被后来的新数据挤到冷数据链表尾部,最终退出buffer pool LRU,根本不会被进入热数据区,如果在冷数据区中被访问到了,就进入到热数据区head指向,然后慢慢挤下去。

在这里插入图片描述
在这里插入图片描述

关于buffer pool的LRU双向链表结构的两个问题,如下:

问题1:为什么buffer pool采用链表这种数据结构?
回答1:因为和缓存一样,它也需要一套淘汰算法来管理数据,Buffer Pool 采用基于 LRU(least recently used 最近使用算法) 的算法来管理内存。

问题2:为什么buffer pool链表的节点大小以页为单位?
回答2:因为MySQL 是以页(page)为单位从磁盘读取数据的,正好,一页一个节点。

4.1.1.3 事务提交,写盘操作

事务提交(对于用户的insert/update/delete操作需要修改数据),写盘操作

这里以一个 update 语句为例,where条件就是找到bufferpool或者datafile里面找,找到后执行器执行,然后修改的新数据写undolog和redolog,然后更新到内存bufferpool,刷盘是由刷脏线程完成的。

在这里插入图片描述

4.1.2 Change Buffer

change buffer是buffer pool的一部分。change buffer以前叫insert buffer,现在更名为change buffer,表示已经兼容update和delete.

change buffer的优点和局限

change buffer优点(或作用):对于非唯一索引,更新一个数据页时,如果数据页在内存中就直接更新,而如果这个数据页还没有在内存中的话,在不影响数据一致性的前提下,InnoDB会将这些更新SQL缓存在change buffer中,这样就不需要从磁盘中读入这个数据页了。在下次查询需要访问这个数据页的时候,将数据页读入内存,然后执行change buffer中与这个页有关的操作,通过这种方式就能保证这个数据逻辑的正确性。

changebuffer局限:对于唯一索引,所有的更新操作都要先判断这个操作是否违反唯一性约束,要判断表中是否存在这个数据,而这必须要将数据页读入内存才能判断,如果都已经读入到内存了,那直接更新内存会更快,就没必要使用change buffer了。即唯一性索引不能放到changebuffer里面,原因在于,如果放到changebuffer里面,而不是从磁盘加载数据判断唯一性,可能会存在主键冲突/唯一性索引冲突。

小结:changebuffer的优点在于减少磁盘IO,changebuffer的局限在于唯一索引不能使用changebuffer.

注意:change buffer在内存中有拷贝,也会被写入到磁盘上。

change buffer本质:如果这个数据页不是唯一索引,不存在数据重复的情况(就是不存在从磁盘加载数据会造主键冲突/唯一性冲突的风险),也就不需要从磁盘加载索引页判断数据是不是重复(即不需要进行唯一性检查)。这种情况下,可以先把 所有的这样的修改记录,保存在内存的缓冲池(指changebuffer)中,从而提升 非唯一性索引的 更新语句(insert delete update)的执行速度。

在这里插入图片描述

对于用户的select操作需要取出数据,如果内存里没有对应「页」的数据,MySQL 就会去把数据从磁盘里 load 出来,如果每次需要的「页」都不同,或者不是相邻的「页」,那么每次 MySQL 都要去 load,这样就很慢了。

Mysql关于change buffer的操作(读盘操作):
对于用户的select操作需要取出数据、update需要修改数据、delete需要删除数据(没有insert,因为insert不用读盘,直接写盘),如果 MySQL 发现你要修改的页,不在内存里,就把你要对页的修改sql语句,先记到内存中一个叫 Change Buffer 的区域,同时记录 redo log,然后再慢慢把数据从磁盘 load 到内存,load 过来后,再把 Change Buffer 里记录的修改sql语句中,放到内存(Buffer Pool)中,这个动作叫做 merge;最后把内存数据刷到磁盘的动作,叫 purge。

merge:Change Buffer -> Buffer Pool
purge:Buffer Pool -> Disk

在这里插入图片描述

The change buffer is a special data structure that caches changes to secondary index pages when those pages are not in the buffer pool. The buffered changes, which may result from INSERT, UPDATE, or DELETE operations (DML), are merged later when the pages are loaded into the buffer pool by other read operations.

Change Buffer 三个需要注意的点:
第一,Change Buffer 只在操作「二级索引」(secondary index)时才使用,原因是「聚簇索引」(clustered indexes)必须是「唯一」的,也就意味着每次插入、更新,都需要检查是否已经有相同的字段存在,也就没有必要使用 Change Buffer 了;
第二,「聚簇索引」操作的随机性比较小,通常在相邻的「页」进行操作,比如使用了自增主键的「聚簇索引」,那么 insert 时就是递增、有序的,不像「二级索引」,访问非常随机。
第三,Change Buffer 的前身是 Insert Buffer,知道就好。

4.1.3 Adaptive Hash Index 自适应哈希索引

对于 MySQL 索引(注意:此时操作对象是索引而不是数据行记录),不管是在磁盘里,还是被 load 到内存后,都是 B+ 树(金手指:索引(特指B+树索引)就是一个B+树),B+ 树的查找次数取决于树的深度。你看,数据都已经放到内存了,还不能“一下子”就找到它,还要“几下子”,这空间牺牲的是不是不太值得?

尤其是那些频繁被访问的数据,每次过来都要走 B+ 树来查询,这时就会想到,我用一个指针把数据的位置记录下来不就好了?
这就是「自适应哈希索引」(Adaptive Hash Index)。自适应,顾名思义,MySQL内部会自动评估使用自适应索引是否值得,如果观察到建立哈希索引可以提升速度,则建立。

4.1.4 Log Buffer

log buffer是一个内存区域,它持有的数据将被写入到磁盘的log文件上。从上面架构图可以看到,Log Buffer 里的 redo log,会被刷到磁盘里:

在这里插入图片描述
所以,即使redo log是顺序io,但是也不是每次都写的,还是在内存中有对应的缓冲区 log buffer,即对于redolog,也是缓存了才写盘。

如下,内存的 log buffer 刷盘的 磁盘的redo log

log buffer,redolog是顺序io,本身是为了保证断电bufferpool中的数据的不丢失,但是redolog也分为内存部分的logbuffer和磁盘部分的redolog,每秒同步,最多丢失一秒数据。

分为 0 1 2 三个值,内存三个组件 buffer pool logbuffer osbuffer 磁盘表示的就是redolog

0 : 每次commit就写入到logbuffer,logbuffer是内存部分,每秒write os cache 且 每秒write redolog,断电,内存最多丢失1秒数据

1 : 每次commit就写入到logbuffer(同时每次commit write redolog,所以write logbuffer没啥用),每次commit write os cache 且 每次 commit write redolog,断电,不丢失任何已提交的数据

2 : 每次commit就写入到logbuffer 且 每次commit write os cache ,os cache 每秒 write redolog,断电,内存最多丢失1秒数据
在这里插入图片描述

默认值是1:每次commit就写入到logbuffer(同时每次commit write redolog,所以write logbuffer没啥用),每次commit write os cache 且 每次 commit write redolog,断电,不丢失任何已提交的数据

在这里插入图片描述

log buffer 就是内存中的redo log buffer,凡是提到log,前面没有加名字,都是表示 redo log .

4.2 操作系统在磁盘前面加一层高速缓存:Operating System Cache

在内存和磁盘之间,你看到 MySQL 画了一层叫做 Operating System Cache 的东西,其实这个不属于 InnoDB 的能力,而是操作系统为了提升性能,在磁盘前面加的一层高速缓存。

4.3 InnoDB 架构第二部分,InnoDB 磁盘架构

磁盘里有什么呢?除了表结构定义和索引,还有一些为了高性能和高可靠而设计的角色,比如 redo log、undo log、Change Buffer,以及 Doublewrite Buffer 等等.

有同学会问,那表的数据呢?其实只要理解了 InnoDB 里的所有表数据,都以索引(聚簇索引+二级索引)的形式存储起来,就知道索引已经包含了表数据。

4.3.1 五个表空间(Tablespaces)

Tablespaces 分为五种:

The System Tablespace 系统表空间:就是ibdata1文件, 系统表在系统表空间里,innodb数据字典(存放系统表的结构定义、元数据)+双写缓冲(页的备份 16KB 4KB)+修改缓冲changebuffer (对应内存的changebuffer)+回滚日志undolog

Undo Tablespaces 撤销表空间:存放undo日志(undo日志既可以存放在系统表空间的undolog里面,也可以存放在undo表空间里面)

General Tablespace 普通表空间:自定义表,每一个表都一个ibd的文件, ts1.ibd ts2.ibd

Temporary Tablespaces 临时表空间:存放SQL语句执行过程中的临时表 ibtmp1文件

File-Per-Table Tablespaces 文件表空间:当innodb _file_per_table=on设置时,自定义表在这个表空间里,每一个表都一个ibd的文件, t1.ibd t2.ibd

我们平时创建的表的数据,可以存放到 The System Tablespace 、File-Per-Table Tablespaces、General Tablespace 三者中的任意一个地方,具体取决于你的配置和创建表时的 sql 语句。

4.3.2 Doublewrite Buffer

如果说 Change Buffer 是提升性能,那么 Doublewrite Buffer 就是保证数据页的可靠性。

对于用户的insert/update/delete操作需要修改数据,MySQL 以页为读取和写入单位,一个页里面有多行数据,写入数据时,MySQL 会先写内存中的页,空了再刷新到磁盘中的页。这时问题来了,假设在某一次从内存刷新到磁盘的过程中,一个页刷了一半,突然操作系统或者 MySQL 进程奔溃了,这时候,内存里的页数据被清除了,而磁盘里的页数据,刷了一半,处于一个中间状态,不尴不尬,可以说是一个不完整,甚至是坏掉的的页。

此情形下redo无用:不是有 Redo Log 么?其实这个时候 Redo Log 也已经无力回天,Redo Log 是要在磁盘中的页数据是正常的、没有损坏的情况下,才能把磁盘里页数据 load 到内存,然后应用 Redo Log。而如果磁盘中的页数据已经损坏,是无法应用 Redo Log 的。

Doublewrite Buffer:所以,MySQL 在刷数据到磁盘之前,要先把数据写到另外一个地方,也就是 Doublewrite Buffer,写完后,再开始写磁盘。Doublewrite Buffer 可以理解为是一个备份(recovery),万一真的发生 crash,就可以利用 Doublewrite Buffer 来修复磁盘里的数据。

InnoDb后台线程

这四个线程是InnoDB的后台线程

在这里插入图片描述
在这里插入图片描述

三个问题

在这里插入图片描述

在这里插入图片描述

四、尾声

MySQL主要分为三大块:连接层、服务层、存储引擎层。
MySQL默认的存储引擎是InnoDB,存储引擎的作用就是用来实践存储的,那么存储在哪里呢?我们要知道数据库是一种系统软件,最终的存储的只能是硬件,要么存储在内存,要么是磁盘,所以InnoDB架构分为内存部分和磁盘部分。

关于InnoDB架构分为内存部分和磁盘部分的问题:其实,不只是mysql,三种常用的关系型数据库mysql sqlserver oracle,三种常用的非关系型数据库 redis mongdb memcached 都一样, 所有的数据库都是软件,要想实现存储,底层还是要依赖硬件的,要么是内存,要么是磁盘/光盘,所有的数据库都分为内存中的数据和持久化到磁盘中的数据。

当然,最常用的关系型就是mysql 最常用的非关系型就是redis,最常用的组合是:redis解决速度问题,弥补mysql的速度劣势。无论是mysql还是redis,都是涉及到持久化的问题的。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
MySQL InnoDB引擎是MySQL数据库的一种存储引擎,它采用了多版本并发控制(MVCC)机制来保证数据的一致性和并发性能。 InnoDB架构的核心组件包括缓冲池、日志缓冲、重做日志、系统表空间和数据文件。 缓冲池是InnoDB中最重要的组件,用于缓存数据和索引页。它预先读取热点数据到内存中,加快查询速度。同时,缓冲池还使用了LRU算法来管理内存页,使得经常被访问的页能够一直保留在内存中,减少IO操作。 日志缓冲用于临时存储已经提交的事务的日志,以提高写操作的性能。它将数据改变操作记录为日志,并在事务提交时将这些操作应用到数据文件中。这种设计可以保证事务的持久性和恢复性。 重做日志是InnoDB实现事务的关键部分。它记录了数据库的所有变动,包括插入、更新和删除操作。它的作用是在需要恢复数据库状态时,通过“重做”操作应用到数据文件中。 系统表空间存储了InnoDB的元数据,包括表定义、索引、MVCC信息等。每个InnoDB表都有一个对应的表空间,用于存储此表的数据和索引。 数据文件是InnoDB存储数据和索引的主要文件,用来持久性地存储数据。它们以页为单位进行管理,每个页一般为16KB大小。 InnoDB架构还支持行级锁和MVCC机制,使得多个事务可以并发地访问数据。行级锁能够降低事务之间的冲突,从而提高并发性能。而MVCC机制则通过保存数据版本信息,可以支持读已提交、可重复读和串行化等不同的事务隔离级别。 综上所述,InnoDB架构MySQL中一种重要的存储引擎,它通过缓冲池、日志缓冲、重做日志和系统表空间等组件实现了高性能、高并发的特性。同时,它也支持行级锁和MVCC机制,提供了灵活的事务隔离级别,使得数据一致性和并发性能得到保证。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

祖母绿宝石

打赏一下

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值