MySQL分层架构分析

名词解释

  • 数据库 : 数据库(DataBase 简称 DB)就是信息的集合或者说数据库是由数据库管理系统管理的数据的集合。
  • 数据库管理系统 : 数据库管理系统(Database Management System 简称 DBMS)是一种操纵和管理数据库的大型软件,通常用于建立、使用和维护数据库。
  • 数据库系统 : 数据库系统(Data Base System,简称 DBS)通常由软件、数据库和数据管理员(DBA)组成。
  • 数据库管理员 : 数据库管理员(Database Administrator, 简称 DBA)负责全面管理和控制数据库系统。

计算机基本组成

  • 元组 : 元组(tuple)是关系数据库中的基本概念,关系是一张表,表中的每行(即数据库中的每条记录)就是一个元组,每列就是一个属性。 在二维表里,元组也称为行。
  • :码就是能唯一标识实体的属性,对应表中的列。
  • 候选码 : 若关系中的某一属性或属性组的值能唯一的标识一个元组,而其任何、子集都不能再标识,则称该属性组为候选码。例如:在学生实体中,“学号”是能唯一的区分学生实体的,同时又假设“姓名”、“班级”的属性组合足以区分学生实体,那么{学号}和{姓名,班级}都是候选码。
  • 主码 : 主码也叫主键。主码是从候选码中选出来的。 一个实体集中只能有一个主码,但可以有多个候选码。
  • 外码 : 外码也叫外键。如果一个关系中的一个属性是另外一个关系中的主码则这个属性为外码。
  • 主属性 : 候选码中出现过的属性称为主属性。比如关系 工人(工号,身份证号,姓名,性别,部门). 显然工号和身份证号都能够唯一标示这个关系,所以都是候选码。工号、身份证号这两个属性就是主属性。如果主码是一个属性组,那么属性组中的属性都是主属性。
  • 非主属性: 不包含在任何一个候选码中的属性称为非主属性。比如在关系——学生(学号,姓名,年龄,性别,班级)中,主码是“学号”,那么其他的“姓名”、“年龄”、“性别”、“班级”就都可以称为非主属性。

主键和外键有什么区别?

  • 主键(主码) :主键用于唯一标识一个元组,不能有重复,不允许为空。一个表只能有一个主键。
  • 外键(外码) :外键用来和其他表建立联系用,外键是另一表的主键,外键是可以有重复的,可以是空值。一个表可以有多个外键。

为什么不推荐使用外键与级联

​ 外键与级联更新适用于单机低并发,不适合分布式、高并发集群; 级联更新是强阻塞,存在数据库更新风暴的风 险; 外键影响数据库的插入速度。还有就是增加了复杂性、增加了额外工作、对分库分表不友好

​ 好处是:

  1. 保证了数据库数据的一致性和完整性;
  2. 级联操作方便,减轻了程序代码量;

什么是 ER 图

E-R 图 也称实体-联系图(Entity Relationship Diagram),提供了表示实体类型、属性和联系的方法,用来描述现实世界的概念模型。 它是描述现实世界关系概念模型的有效方法。 是表示概念关系模型的一种方式。

什么是存储过程

​ 存储过程看成是一些 SQL 语句的集合,中间加了点逻辑控制语句。存储过程在业务比较复杂的时候是非常实用的,比如很多时候我们完成一个操作可能需要写一大串 SQL 语句,这时候我们就可以写有一个存储过程,这样也方便了我们下一次的调用。存储过程一旦调试完成通过后就能稳定运行,另外,使用存储过程比单纯 SQL 语句执行要快,因为存储过程是预编译过的。适用于DB开发工程师。

MySQL

​ MySQL 数据库区别于其他数据库的最重要的一个特点就是其插件式的表存储引擎存储引擎是基于表的,而不是数据库。用户可以根据 MySQL 预定义的存储引擎接口编写自己的存储引擎。

MySQL分层架构

​ MySQL 有点与众不同,它的架构可以在多种不同场景中应用并发挥良好作用。主要体现在存储引擎的架构上,插件式的存储引擎架构将查询处理和其它的系统任务以及数据的存储提取相分离MySQL的架构分为连接层、服务层、引擎层、存储层。

MySQL主要分为 Server层 和 存储引擎层

Server层:主要包括连接器、查询缓存(MySQL8.0移除)、分析器、优化器、执行器等,所有的跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图、函数等,还有一个通用的日志模块binglog
存储引擎:主要负责数据的存储和读取,采用可以替换的插件式架构,支持InnoDB、MyISAM、Memory等多个存储引擎,其中InnnoDB有属于自己的日志模块。现在最常用的存储引擎是InnoDB,它从MySQL5.5.5版本开始被当做默认的存储引擎了。

MySQL的架构图

一、连接层/网络层

​ 是一些客户端和连接服务,包含本地 socket 通信和大多数基于客户端/服务端工具实现的类似于 tcp/ip 的通信。主要完成一些类似于建立连接、授权认证、及相关的安全方案

特性

1)Poll、Select模型

​ mysql在启动后,创建了socket server,绑定了3306端口,并对其进行监听。和java里的写法类似,用一个while循环来监听新来的connection,如果有了新连接,就创建一个新的连接线程去处理(不能超过max-connections)。这种就是典型的BIO的模式,为每一个连接创建一个线程。并且mysql在这里采用了IO多路复用,会有一个if语句来判断当前系统是否支持Poll模式,否则就走Select模型(各个系统默认都有实现)。

​ Poll模型和Select非常类似,只是少了1024个fd的限制,都是采用遍历数组轮询有没有新IO事件的方式,在连接数较少的情况下,性能优异,要好于epoll。

2) Max-connections

​ mysql基于BIO模式,本质上是不接受大量的socket连接的,所以设置了max-connections这个限制,超过设置的max,那么新来的连接会被拒绝。DB采用session作为一个连接会话,这一个session里,SQL的执行必须是串行、同步、有序的,而不能是异步乱序的。一个session内可能有多个操作,增删改查、事务隔离,必须保证顺序不能乱。

3) connection复用

mysql能支撑的连接数是有限的,那么就需要应用程序来利用好连接池。对于连接池来说,就是做好仅限的连接数的管理就好。那么就有了连接复用的模式。

Mysql连接基本原则:IO密集的时候,要减少连接数。

二、服务层/SQL层

包括查询解析、优化、缓存、以及所有的内置函数,所有跨存储引擎的功能也都在这一层实现,包括触发器、存储过程、视图等。

1)查询缓存

​ MySQL 拿到一个查询请求后,会先到查询缓存看看,之前是不是执行过这条语句。之前执行过的语句及其结果可能会以 key-value 对的形式,被直接缓存在内存中。如果没有命中查询缓存,就要开始真正执行语句了。

注意:

  • 只要该表的结构或者数据被修改,那使用该表的所有高速缓存查询都将变为无效并从缓存中删除
  • 虽然可以提升系统性能,但也不得不因维护这块缓存而造成一些开销。从MySQL 5.7.20开始,不推荐使用查询缓存,并在MySQL 8.0中删除。

​ MySQL查询缓存是MySQL中比较独特的一个缓存区域,用来缓存特定Query的整个结果集信息,且共享给所有客户端。为了提高完全相同的Query语句的响应速度,MySQL 会对查询语句进行hash计算后,把得到的hash值与Query查询的结果集对应存放在Query Cache中

​ 当开启Query Cache之后,MySQL 会对接收到的每一个SELECT语句通过特定的hash算法计算该Query的hash值,然后通过该hash值到Query Cache中去匹配。如果通过hash值匹配到了一样的Query,则直接将cache中相应的Query结果集返回给客户端。

​ 由于Query Cache是针对SELECT语句的hash值作为key值进行存储的,意味着SQL语句哪怕出现一个字符的不同,缓存也无法进行命中。

Query Cache的劣势

1、查询SQL的命中。Query Cache是基于字节级别的,只要有一点点误差就会导致无法命中。

2、缓存过期。Query Cache的淘汰策略过于苛刻,任何对于表中数据的修改,都会使得缓存失效。

3、分区表禁用。如果数据表使用了分区,Query Cache将会被自动的禁用,无法生效

4、增加额外的负载。当开启Query Cache选项后,如果查询请求没有命中Query Cache时,MySQL会需要额外的性能开销去处理结果集,写入Query Cache中,最糟糕的情况下,这个额外的性能开销是13%

2)分析器

​ **分析器先会做“词法分析”,词法分析完后就要做“语法分析”。**根据词法分析的结果,语法分析器会根据语法规则,判断你输入的这个 SQL 语句是否满足 MySQL 语法。如果你的语句不对,就会收到“You have an error in your SQL syntax”的错误提醒。分析完了将要查询的表、各种查询条件都提取出来放到MySQL服务器内部使用的一些数据结构上来。

分析器收到一条SQL,其执行过程:词法解析,语法分析,构造执行树,生成执行计划,计划的执行。

​ 一般来说,词法和语法解析都是通过 Flex 与 Bison 完成的;而在 MySQL 中,词法分析使用自己的程序,而语法分析使用的是 Bison;Bison 会根据 MySQL 定义的语法规则,进行语法解析。完成语法解析后,会将解析结果生成的数据结构保存在 struct LEX 中,该结构体在 sql/sql_lex.h 文件中定义。

解析过程:

1、语法解析。SQL 解析入口会调用 MYSQLparse ,而在 sql/sql_yacc.cc 中有如下的宏定义,也就说,在预编译阶段,会将 yyparse 替换为 MYSQLparse ,所以 实际调用的仍是 yyparse 函数

2、词法解析。MYSQL 的词法分析并没有使用 LEX,而是有自己的一套词法分析,代码详见 sql/sql_lex.cc 中的实现,其入口函数是 MYSQLlex()

3)优化器

​ 经过了分析器,MySQL就知道你要做什么了。在开始执行之前,还要先经过优化器的处理。优化器会根据解析结果体 sql/sql_lex.h 文件, 找出执行该语句所有可能使用的执行方案,并找到其中成本最低的方案,这个成本最低的方案就是所谓的执行计划,之后才会真正的执行查询

执行方案包括:
  1. 全表扫描
  2. 找出查询语句中所有可能使用的索引,使用不同索引进行查询

执行方案的成本计算:

**1)IO 成本:从磁盘中加载数据页需要的成本。**MySQL 中规定了成本常数,读取一个页的成本默认是1

2)CPU 成本:读取记录、将记录与条件进行比较、排序等操作都算作 CPU 成本,一条记录的读取或比较成本是0.2

成本计算:

1)全表扫描

​ **首先计算全表扫描的 IO 成本,全表扫描访问聚簇索引。**因此要计算 IO 成本就需要估算出聚簇索引占用的页面数。这个信息可以从表的统计信息中得到,里面有一个Data_length字段表示聚簇索引的存储空间大小,用Data_length除以 MySQL 默认 16KB 的页面大小,就能得到聚簇索引占用的页面数了。用这个数乘以成本常数1再加上微调(忽略就行),就是 MySQL 计算的 IO 成本。

2)索引+回表

如果通过索引的方式来执行查询,需要计算下面这些成本

2-1)根据索引列的查询条件通过二级索引定位主键ID。通过这种方式大概估计出记录条数,得到记录条数之后,乘以CPU成本常数0.2就得到了CPU成本。IO 成本的计算比较简单,MySQL 认为一个范围区间的IO成本就是1。

2-2)对这些主键ID进行回表。回表可以看作是随机 IO。 根据上面估算的记录条数,MySQL 把每条记录的 IO 成本都看作是访问一个页面的成本,所以回表操作的IO成本就是记录条数乘以IO成本常数

​ **2-3)回表得到的记录用其他查询条件进行过滤。**这一步只考虑 CPU 成本,直接用之前预估的记录条数乘以 CPU 成本常数。

3)其他(表连接的成本计算)

​ 上面的内容是针对单次连接,因此连接查询的成本就是驱动表访问一次的成本加上被驱动表访问多次的成本。对于内连接,驱动表和被驱动表的位置是可以互换的,而不同的表作为驱动表的查询成本可能是不同的

4)执行器

​ 进入了执行器阶段,开始执行的时候,要先判断一下你对这个表有没有执行查询的权限,如果没有,就会返回没有权限的错误。如果有权限,就打开表继续执行。打开表的时候,执行器就会根据表的引擎定义,去使用这个引擎提供的接口。MySQL根据优化器生成的执行计划,调用存储引擎的API来执行查询。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

5)SQL接口

用于接收客户端发送的各种 SQL 命令,返回用户需要查询的结果,比如 DML、DDL、存储过程、视图、触发器这些。

5)存储引擎层

​ **存储引擎层,存储引擎真正的负责了 MySQL 中数据的存储和提取,服务器通过 API 与存储引擎进行通信。**不同的存储引擎具有的功能不同,这样我们可以根据自己的实际需要进行选取。MySQL常见的存储引擎有InnoDB、MyISAM。从MySQL5.5.5版本开始被当做默认的存储引擎了。

InnoDB存储引擎

InnoDB是基于磁盘存储的引擎,其真正的数据最终都是保存在文件中的,并且以页为最小存储单位。而真正处理数据的过程是发生在内存中的,所以需要把磁盘中的数据加载到内存中,如果是处理写入或修改请求的话,还需要把内存中的内容刷新到磁盘上。

InnoDB存储引擎主要由两个部分组成,分别是内存架构磁盘架构,这两个部分都有自己不可或缺的功能。

​ 内存架构(英文名称:In-Memory Structures),InnoDB存储引擎中主要包括四个部分,分别是自适应哈希索引、Buffer poolChange bufferLog Buffer四个部分

磁盘架构也就是表空间。InnoDB存储引擎的表空间主要分为:系统表空间、独立表空间、普通表空间、Undo表空间以及临时表空间。底层逻辑架构

​ InnoDB的内存结构和磁盘存储结构图总结如下:

image.png

内存架构-自适应哈希索引

InnoDB会根据访问的频率和模式,为热点页建立哈希索引,来提高查询效率。InnoDB存储引擎会监控对表上各索引页的查询。自适应哈希索引(AHI)是通过缓冲池的B+树页构造而来,因此建立的速度很快,而且不需要对整张表构建哈希索引。 InnoDB存储引擎会自动根据访问的频率和模式来自动地为某些热点页建立哈希索引。

建立条件

​ 1)索引被查询的次数足够多。如果查询次数用N1来表示的话那么N1应该大于17(N1>17),此过程无时间限制。

​ 2)基于查询的sql语句创建一个hash信息(hash info)其中包含了三部分内容:

​ 2-1)匹配索引的列数

​ 2-2)下一列匹配的字节数

​ 2-3)是否从左匹配

​ 如果根据此条件生成的hash info被使用的次数足够多 (N2 > 100)

​ 3)生成的hash info能够命中某个数据页,且命中的该页上的记录数要大于该页上总记录数的1/16(N3 > 页记录数的1/16)

​ 满足以上三点要求那么就会将hash info生成一个key,value就是指向数据页上记录的指针。这样就建立起了mysql的自适应哈希索引

内存架构-Buffer pool缓冲池

​ 如下所说,或者看缓冲池篇

内存架构-Change buffer写缓存

写缓冲主要的功能是记录数据库的数据修改操作的结果的。主要目的是提高数据库的写性能

内存架构-Log Buffer日志缓存

​ 日志缓冲区(Log Buffer)是MySQL中用于存储事务日志(Transaction Log)的缓冲区。它在数据库执行事务时起到关键的作用。当在MySQL中对InnoDB表进行更改时,这些更改首先存储在InnoDB日志缓冲区的内存中,然后写入通常称为重做日志(redo logs)的InnoDB日志文件中。

redo日志缓冲区是内存存储区域,用于保存要写入磁盘上的日志文件的数据。日志缓冲区大小由innodb_log_buffer_size 变量定义,默认大小为16MB。

磁盘架构-系统表空间

系统表空间是InnoDB存储引擎中最重要的表空间之一,它的主要作用是存储InnoDB数据字典、双写缓冲、更改缓存以及撤销日志。

系统表空间一般存放于 MySQL 数据库目录中,名称为:ibdata1。系统表空间一般不一定只有一个,也可能有多个,系统表空间的大小和数量由innodb_data_file_path控制。InnoDB 数据字典在 MySQL 8.0 版本以后合并至 MySQL 数据字典中了,不再存储在系统表空间中了。

磁盘架构-独立表空间

​ 对于innodb存储引擎来说,我们通常创建数据表的时候,会在 MySQL 数据目录中创建两个文件,分别是.ibd.frm两个文件。.ibd文件主要用来存储表数据,而.frm文件主要用来存储索引

​ 这种做法可以将所有的数据表分开管理,也能够实现快速数据迁移,当数据出现故障之时也可以提高数据恢复的成功率。不过这样的做法又会增加磁盘的碎片,对系统处理表文件的性能有一定的影响

磁盘架构-普通表空间

普通表空间的本质其实就是一个共享的表空间。其具体文件在 MySQL 数据库的数据目录中是以.ibd结尾的文件。跟系统表空间类似,它支持所有 MySQL 数据库中的数据表的结构,它是将数据库的一些元数据保存在内存之中,进而能够减少独立表空间对于内存的消耗。

磁盘架构-Undo 表空间

Undo 表空间主要是用来保存撤销日志(即:Undo Log)的空间。它默认情况下存储在 MySQL 数据库的根目录。在MySQL 8.0版本之后,undo 表空间会在 MySQL 数据库的数据根目录生成 undo_001undo002 共两个文件。

磁盘架构-临时表空间

临时表空间主要是用来保存数据库会话中的临时数据的。在 MySQL 数据库的数据根目录中保存以ibtmp1命名的文件。最主要的是我们在使用 join 连表查询的时候,会在临时表空间内创建临时数据表用来辅助查询

小结

​ 在内存架构中,自适应哈希索引有利于提高查询速度;Buffer pool主要提供了一个内存池,将经常查询的数据存放于内存中,这样做有利于提高数据库的查询性能和降低系统的磁盘 IOChange buffer主要是将修改好的数据存放于内存之中,下一次查询的时候合并到Buffer pool之中,这样做的好处是可以降低修改数据时的磁盘 IO,进而提高数据库的性能;Log Buffer是将所有修改的数据存放在其中,之后写入到Redo Log之中,防止数据丢失。

大概来说,MySQL的数据变更:Buffer Pool-》Change Buffer-》Log Buffer-》Redo log-》磁盘

​ 在磁盘架构中,系统表空间是用来修改和撤销日志的地方,之前的数据库版本中还存放InnoDB数据字典以及双写缓冲独立表空间主要是用来存储表数据和索引的地方;普通表空间是一个共享的表空间,能够减少独立表空间对于内存的消耗;Undo 表空间主要作用于事务回滚的,在使用未提交之前,用来保存原来的数据,一旦事务回滚则用 Undo 表空间中的内容替换修改过后的数据,进而达到回滚的目的;临时表空间主要是一个过渡的表空间,通常的一些操作需要有这种过渡来辅助操作,例如连表查询。

​ 从内存架构到磁盘架构,InnoDB存储引擎为我们提供了一个高性能、高安全的数据库存储引擎。通常在实际应用过程中,InnoDB存储引擎是我们的首选存储引擎,但是在使用过程中一定要把Buffer pool的空间设置得足够大,这样有利于提高数据的查询性能


常见的日志(InnoDB)

MySQL常见的日志类型主要有下面几类(针对的是 InnoDB 存储引擎):

  • 错误日志(error log) :对 MySQL 的启动、运行、关闭过程进行了记录。
  • 二进制日志(binary log,binlog) :主要记录的是更改数据库数据的 SQL 语句。
  • 一般查询日志(general query log) :已建立连接的客户端发送给 MySQL 服务器的所有 SQL 记录,因为 SQL 的量比较大,默认是不开启的,也不建议开启。
  • 慢查询日志(slow query log) :执行时间超过 long_query_time秒钟的查询,解决 SQL 慢查询问题的时候会用到。(没有使用索引的sql语句同样会被记录在慢查询日志里)
  • 事务日志(redo log 和 undo log) :redo log 是重做日志,undo log 是回滚日志。
  • 中继日志(relay log) :relay log 是复制过程中产生的日志,很多方面都跟 binary log 差不多。不过,relay log 针对的是主从复制中的从库。
  • DDL 日志(metadata log) :DDL 语句执行的元数据操作。
1)慢查询日志

慢查询日志记录了执行时间超过 long_query_time(默认是 10s,通常设置为1s)的所有查询语句。找到慢 SQL 是优化 SQL 语句性能的第一步,然后再用EXPLAIN 命令可以对慢 SQL 进行分析,获取执行计划的相关信息。

注意:

  • 通过 show variables like “slow_query_log”;命令来查看慢查询日志是否开启,默认是关闭的。
  • 通过 SET GLOBAL slow_query_log=ON 命令将其开启。long_query_time 参数定义了一个查询消耗多长时间才可以被定义为慢查询,默认是 10s,通过 SHOW VARIABLES LIKE '%long_query_time%'命令即可查看。

在实际项目中,慢查询日志可能会比较大,一般都是借助 MySQL 官方的慢查询分析调优工具 mysqldumpslow。另外在MySQL中有一个变量专门记录当前慢查询语句的个数,可以通过 show global status like ‘%Slow_queries%’; 命令查看。

2)binlog (二进制日志)

二进制日志文件,这个文件记录了MySQL所有的DML操作。通过binlog日志我们可以做数据恢复,增量备份,主主复制和主从复制等等

3)redo log(重做日志)

重做日志。会记录变更了数据页里的几个字节的值。表空间+数据页号+偏移量+修改几个字节的值+具体的值

4)undo log(回滚日志)

​ undo log它记录着事务回滚前的数据。undo 日志只记录事务中的增删改操作。
补充说明:三大日志详细内容看日志篇。

InnoDB的内存管理

一、额外的内存池

在InnoDB存储引擎中,对内存的管理是通过一种称之为内存堆的方式进行的,在对一些数据结构本身的内存进行分配时,需要从额外的内存池中进行申请,当该区域的内存不够时,会从缓冲池中申请。

二、后台线程

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

1)主线程Master Thread

主线程负责将缓冲池中的数据异步刷新到磁盘,保证数据的一致性,包括脏页的刷新、合并插入缓冲、undo页的回收等等

Master Thread具有最高的线程优先级别,其内部由多个循环组成:主循环Loop、后台循环Background Loop、刷新循环Flush Loop、暂停循环Suspend Loop。Master Thread会根据数据库的状态在主循环、后台循环、刷新循环、暂停循环中切换。

主循环Loop有两大操作——每秒和每10秒,并且是通过线程的sleep实现循环的,可见当负载很大时,会出现延迟的情况,所以并不是很精确的每秒和每10秒操作

每秒的操作如下:

  • 日志缓冲写入到磁盘:即使事务还未提交,都会每秒将重做日志缓冲区的内容写入到重做日志文件
  • 合并插入缓冲:先判断前一秒发生的IO次数是否小于5,如果小于5则认为IO压力很小,可以进行合并插入缓冲的操作
  • 至多写入100个缓冲池中的脏页到磁盘:InnoDb会判断当前缓冲池中脏页的比例(buf_get_modified_ratio_pct)是否超过了参数innodb_max_dirty_pages_pct(默认为90,代表90%),如果超过则认为需要做磁盘同步操作,就将100个脏页写入磁盘
  • 如果当前没有用户活动,则切换到background loop

注意

插入缓冲是指插入数据前,非聚集索引会先缓存到缓冲池的插入缓冲区,然后按一定频率刷新到数据库,并非每插入一条,就更新一次索引树,这样效率同样很低。

每10秒的操作如下:

  • 写入100个脏页到磁盘:InnoDB会先判断前10秒内的IO操作次数是否小于200,是则认为磁盘IO压力较小,可以将100个脏页写到磁盘。
  • 合并至多5个插入缓冲
  • 日志缓冲写入到磁盘
  • 删除无用的undo页
  • 写入100个或10个脏页到磁盘:如果缓冲池中的脏页比例(buf_get_modified_ratio_pct)超过70%,会刷新100个脏页,否则刷新10个脏页
2)读写线程IO Thread

​ 在InnoDB中大量使用了异步IO(Async IO)来处理读写请求,而IO Thread就是负责处理这些请求的回调。在InnoDB 1.0版本 之前共有4个读写线程:write、read、insert buffer和log IO。在InnoDB 1.0之后write和read分别增大到4个,可以使用innodb_read_io_threadsinnodb_write_io_threads参数进行设置

3)回收线程Purge Thread

​ 当事务成功提交后,事务所关联的undo log已经不再需要,故需要使用回收线程去回收所分配的undo页。回收线程的数量默认为1个,可以通过参数innodb_purge_threads进行配置

4)脏页清除线程Page Cleaner Thread

​ 这是从主线程中分离出来,专门用于刷新脏页到磁盘的线程,其目的是减轻Master Thread的工作及对于用户查询线程的阻塞,进一步提高性能。

小结

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

磁盘结构

前言

​ 数据在InnoDB采取的方式是**将数据划分为若干个页,以页作为磁盘和内存之间交互的基本单位,InnoDB中页的大小一般为 16 KB。也就是在一般情况下,一次最少从磁盘中读取16KB的内容到内存中,一次最少把内存中的16KB内容刷新到磁盘中。**当记录中的数据太多,当前页放不下的时候,会把多余的数据存储到其他页中,这种现象称为行溢出。

行格式

​ 我们平时是以记录为单位来向表中插入数据的,这些记录在磁盘上的存放方式也被称为行格式。InnoDB存储引擎设计了4种不同类型的行格式,分别是Compact、Redundant、Dynamic和Compressed行格式。COMPACT行格式示意图如下:

image.png

MySQL5.7的默认行格式就是Dynamic,Dynamic和Compressed行格式和Compact行格式挺像,只不过在处理行溢出数据时有所不同。Compressed行格式和Dynamic不同的一点是,Compressed行格式会采用压缩算法对页面进行压缩,以节省空间

索引页格式

页是InnoDB管理存储空间的基本单位,一个页的大小一般是16KB。InnoDB为了不同的目的而设计了许多种不同类型的页,这种存放记录的页为索引(INDEX)页,不过要理解成数据页也没问题,毕竟存在着聚簇索引这种索引和数据混合的东西。

一个InnoDB数据页的存储空间大致被划分成了7个部分:

  • File Header 文件头部 38字节 页的一些通用信息
  • Page Header 页面头部 56字节 数据页专有的一些信息
  • Infimum + Supremum 最小记录和最大记录 26字节 两个虚拟的行记录
  • User Records 用户记录 大小不确定 实际存储的行记录内容
  • Free Space 空闲空间 大小不确定 页中尚未使用的空间
  • Page Directory 页面目录 大小不确定 页中的某些记录的相对位置
  • File Trailer 文件尾部 8字节 校验页是否完整

表空间

InnoDB是以页为单位管理存储空间的,聚簇索引(也就是完整的表数据)和其他的二级索引都是以B+树的形式保存到表空间的,而B+树的节点就是数据页

​ 任何类型的页都有File Header这个部分,File Header中专门的地方(FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID)保存页属于哪个表空间,同时表空间中的每一个页都对应着一个页号(FIL_PAGE_OFFSET),这个页号由4个字节组成,也就是32个比特位,所以一个表空间最多可以拥有2³²个页,如果按照页的默认大小16KB来算,一个表空间最多支持64TB的数据

在 InnoDB 存储引擎中,所有的数据都被逻辑地存放在表空间中,表空间(tablespace)是存储引擎中最高的存储逻辑单位,在表空间的下面又包括段(segment)、区(extent)、页(page)。默认情况下,表空间中的页大小都为 16KB,当然也可以通过改变 innodb_page_size 选项对默认大小进行修改。一个区的大小最小为 1MB,页的数量最少为 64 个。

​ 数据存储层,主要是将数据存储在运行于该设备的文件系统之上,并完成与存储引擎的交互。InnoDB存储表时会将表的定义和数据索引等信息分开存储,其中表的定义存储在 .frm 文件中,数据索引存储在 .ibd 文件中。

image.png

其他说明

以后补充吧

InnoDB的三大特性

1、双写机制/双写缓冲区。

Doublewrite Buffer(双写缓冲)的出现就是为了解决服务宕机,redo log不完整的问题。Doublewrite Buffer 双写缓冲是内存+磁盘的结构。

Doublewrite Buffer是一种特殊文件flush技术,带给InnoDB存储引擎的是数据页的可靠性。它的作用是,在把页写到数据文件之前,InnoDB先把它们写到一个叫doublewrite buffer(双写缓冲区)的共享表空间内,在写doublewrite buffer完成后,InnoDB才会把页写到数据文件的适当的位置。如果在写页的过程中发生意外崩溃,InnoDB在稍后的恢复过程中在doublewrite buffer中找到完好的page副本用于恢复。

​ 所以在正常的情况下,MySQL写数据页时,会写两遍到磁盘上,第一遍是写到doublewrite buffer,第二遍是写到真正的数据文件中,这就是“Doublewrite”的由来。

Doublewrite Buffer内存结构由128个页(Page)构成,大小是2MB。Doublewrite Buffer磁盘结构在系统表空间上是128个页(2个区,extend1和extend2),大小是2MB。

双写缓冲区是如何恢复数据的?

​ 双写缓冲区存在的目的就是 Innodb 为了保证 MySQL 数据的原子性。在数据库异常关闭的情况下启动时,都会做数据库恢复(redo)操作,恢复的过程中,数据库都会检查页面是不是合法(校验等等),如果发现一个页面校验结果不一致,则此时会用到双写这个功能,并对有问题的页进行数据恢复

当有页数据要刷盘时:

  1. 页数据先通过memcpy函数拷贝至内存中的Doublewrite Buffer中;
  2. Doublewrite Buffer的内存里的数据页,会fsync刷到Doublewrite Buffer的磁盘上,分两次写入磁盘共享表空间中(连续存储,顺序写,性能很高),每次写1MB;
  3. Doublewrite Buffer的内存里的数据页,再刷到数据磁盘存储.ibd文件上(离散写);
小结

​ InnoDB Doublewrite Buffer是InnoDB的一个重要特性,用于保证MySQL数据的可靠性和一致性。它的实现原理是通过将要写入磁盘的数据先写入到Doublewrite Buffer中的内存缓存区域,然后再写入到磁盘的两个不同位置,来避免由于磁盘损坏等因素导致数据丢失或不一致的问题。Doublewrite Buffer对于保证MySQL数据的安全性和一致性具有重要意义。

2、Buffer Pool 缓冲池。

​ 用于缓存数据库查询出来的数据,便于下次快速查询。

Buffer Pool 缓冲池。InnoDB 为了缓存磁盘中的页,在 MySQL 服务器启动的时候就向操作系统申请了一片连续的内存,叫做 Buffer Pool。默认情况下 Buffer Pool 只有 128M 大小。启动服务器的时候可以通过配置 innodb_buffer_pool_size 参数的值来控制 Buffer Pool 大小。缓存页大小和在磁盘上默认的页大小是一样的,都是 16KB。

​ InnoDB 为每一个缓存页都创建了一些所谓的控制信息,包括该页所属的表空间编号、页号、 缓存页在 Buffer Pool 中的地址、链表节点信息、一些锁信息以及 LSN 信息,当然还有一些别的控制信息,我们称之为控制块。

缓冲页的链表结构有Free 链表、Flush 链表、LRU 链表。Free 链表主要是空闲的缓冲页;Flush 链表为脏链;LRU 链表是淘汰链(用来淘汰不常使用的数据),假如链表满了,需要淘汰,就从链表尾淘汰。

​ 详细内容看Buffer Pool 缓冲池篇

3、自适应哈希索引。

InnoDB内部监控索引表,如果被认为是热数据,会自动创建自适应索引。创建以后,如果下次又查询到这个索引, 那么直接通过 hash 算法推导出记录的地址。用于提高 MySQL 的查询效率。

自适应哈希索引。MySQL 在哈希索引的设计上还采用了热点分散技术,这样的哈希索引在 MySQL 上默认是启动 8 个的,热点数据会分散到不同的哈希索引上,因此热数据访问时,能将请求分散到不同的哈希索引上,提高了并发访问的性能。

MyISAM存储引擎

​ 每个MyISAM表存储在磁盘上的三个文件中 。这些文件的名称以表名开头,并有一个扩展名来指示文件类型 。.frm文件存储表的格式。 .MYD (MYData) 文件存储表的数据。 .MYI (MYIndex) 文件存储索引。**

​ **MyISAM 提供了大量的特性,包括全文索引、压缩、空间函数(GIS)等,但 MyISAM 不支持事务和行级锁,而且有一个毫无疑问的缺陷就是崩溃后无法安全恢复。**但是对于只读的数据,或者表比较小、可以忍受修复(repair)操作,则依然可以继续使用 MyISAM。

MySQL 对 LRU 改进措施:

1、将 LRU 链表分为两部分,前面为热数据去(Young 区),后面为冷数据区(Old 区),Old 区大小占 37%。优点:冷热链的切分,排除了全表扫描等类似的大数据量查询,直接把热门数据淘汰出缓冲区的情况。

2、对冷链数据移动到热链上做了时间限定。限定时间内对冷链上数据的访问不会移动到热数据区,只有超过这个时间,再次访问冷链上的数据,才会被移动到热数据区。

MyISAM具有的特征

1、每个MyISAM表最大索引数是64,这可以通过重新编译来改变。每个索引最大的列数是16。

2、可以将数据文件和索引文件放在不同物理设备上的不同目录中,以更快地使用数据目录和索引目录表选项来创建表。

3、BLOB和TEXT列可以被索引。

4、NULL被允许在索引的列中

5、等等

存储引擎对比

MyISAM 和 INNODB 的区别(重点):

  1. 事务安全(MyISAM不支持事务,INNODB支持事务);
  2. 外键 MyISAM 不支持外键, INNODB支持外键;
  3. 锁机制(MyISAM时表锁,innodb是行锁);
  4. 查询和添加速度(MyISAM批量插入速度快);
  5. 支持全文索引(MyISAM支持全文索引,INNODB不支持全文索引);
  6. MyISAM内存空间使用率比InnoDB低
  7. MyISAM的索引结构是B tree(B-tree) 索引,INNODB的是B+tree

其他

查看存储引擎

-- 查看支持的存储引擎
SHOW ENGINES
  - Support列表示该存储引擎是否可用
  - DEFAULT值代表是当前服务器程序的默认存储引擎
  - Transactions列代表该存储引擎是否支持事务处理
  - Comment列是对存储引擎的一个描述
  - XA列代表着该存储引擎是否支持分布式事务
  - Savepoints代表着该存储引擎是否支持部分事务回滚

-- 查看默认存储引擎
SHOW VARIABLES LIKE 'storage_engine'

--查看具体某一个表所使用的存储引擎,这个默认存储引擎被修改了!
show create table tablename

--准确查看某个数据库中的某一表所使用的存储引擎
show table status like 'tablename'
show table status from database where name="tablename"

设置存储引擎

-- 建表时指定存储引擎。默认的就是INNODB,不需要设置
CREATE TABLE t1 (i INT) ENGINE = INNODB;
CREATE TABLE t2 (i INT) ENGINE = CSV;
CREATE TABLE t3 (i INT) ENGINE = MEMORY;

-- 修改存储引擎
ALTER TABLE t ENGINE = InnoDB;

-- 修改默认存储引擎,也可以在配置文件my.cnf中修改默认引擎
SET default_storage_engine=NDBCLUSTER;

重要的字符集

  • ASCII

    • 128个字符,包括空格、标点符号、数字、大小写字母和一些不可见字符
  • ISO 8859-1

    • 256个字符,是在ASCII字符集的基础上又扩充了128个西欧常用字符
  • GB2312

    • 汉字以及拉丁字母、希腊字母、日文平假名及片假名字母、俄语西里尔字母

    • 兼容ASCII字符集

      • 如果该字符在ASCII字符集中,则采用1字节编码
  • GBK

    • 范围上对GB2312字符集作了扩充,编码方式上兼容GB2312
  • utf8

    • 收录地球上能想到的所有字符,而且还在不断扩充。这种字符集兼容ASCII字符集,采用变长编码方式,编码一个字符需要使用1~4个字节

MySQL支持的字符集和排序规则

  • utf8和utf8mb4

    • utf8字符集表示一个字符需要使用1~4个字节,常用的一些字符使用1~3个字节就可以表示了

    • MySQL中字符集表示一个字符所用最大字节长度在某些方面会影响系统的存储和性能

      • utf8mb3:阉割过的utf8字符集,只使用1~3个字节表示字符
      • utf8mb4:正宗的utf8字符集,使用1~4个字节表示字符。
    • 在MySQL中utf8是utf8mb3的别名,在MySQL中提到utf8就意味着使用1~3个字节来表示一个字符

    • 有使用4字节编码一个字符的情况,比如存储一些emoji表情啥的,应该使用utf8mb4。

字符集的查看

  • SHOW CHARSET;

比较规则的查看

  • SHOW COLLATION LIKE ‘utf8_%’

4个级别的字符集和比较规则

  • 服务器级别

    • SHOW VARIABLES LIKE ‘character_set_server’;
    • SHOW VARIABLES LIKE ‘collation_server’;
  • 数据库级别

    • SHOW VARIABLES LIKE ‘character_set_database’;
    • HOW VARIABLES LIKE ‘collation_database’;
    • 在创建和修改数据库的时候可以指定该数据库的字符集和比较规则

总结

​ InnoDB的整体架构类似Java程序架构,但是多了一层数据存储,存储是在磁盘上面。而对磁盘的读写是非常耗费IO,也就有了内存结构的划分。为了读写方面的问题,有了缓冲池;为了奔溃恢复,有了重做日志缓冲;但是由于考虑避免在内存分配过程中,空间不足,但是也不能像Java一样OOM,有了额外内存池用于分配资源;而对数据的处理是基于内存,但是最终要落库,便有的后台线程的存在,负则刷盘到磁盘中;读写;垃圾回收;脏页清理等。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值