这里写目录标题
- MySQL架构
- 一条sql执行流程
- MySQL数据存放电脑位置
- ibd文件结构
- 行溢出是什么
- MySQL行记录存储格式
MySQL架构
MySQL 的架构共分为两层:Server 层和存储引擎层
Server层
Server 层主要负责建立连接、分析和执行 SQL。MySQL 里大多数的核心功能模块都在这实现,主要包括连接器,查询缓存、解析器、预处理器、优化器、执行器等。另外,所有的内置函数和所有跨存储引擎的功能比如存储过程、触发器等都在 Server 层实现。
存储引擎层
存储引擎层主要负责数据的存储和提取。MySQL支持多个存储引擎,不同的存储引擎共用一个 Server 层。从 MySQL 5.5 版本开始, InnoDB 成为了 MySQL 的默认存储引擎。我们常说的索引数据结构,就是由存储引擎层实现的,不同的存储引擎支持的索引类型也不相同,比如 InnoDB 支持索引类型是 B+树 ,且是默认使用,也就是说在数据表中创建的主键索引和二级索引默认使用的是 B+ 树索引。
一条sql执行流程
第一步:通过连接器进行连接
我们首先需要连接 MySQL 服务器,然后才能执行 SQL。因为 MySQL 是基于 TCP 协议进行传输的,所以连接的过程需要先经过 TCP 的三次握手。
第二步:查询缓存
如果 SQL 是查询语句,MySQL 就会先去缓存里查找缓存数据,看看之前有没有执行过这一条命令,这个查询缓存是以 key-value 形式保存在内存中的,key 为 SQL 查询语句,value 为 SQL 语句查询的结果。
如果查询的语句命中查询缓存,那么就会直接返回 value 给客户端。如果查询的语句没有命中查询缓存中,那么就要往下继续执行,等执行完后,查询的结果就会被存入查询缓存中。
但其实对于更新比较频繁的表,查询缓存的命中率很低的,因为只要一个表有更新操作,那么这个表的查询缓存就会被清空。如果刚缓存了一个查询结果很大的数据,还没被使用的时候,刚好这个表有更新操作,查询缓冲就被清空了,那这样也太浪费资源了。所以,MySQL 8.0 版本直接将查询缓存删掉了。
第三步:解析器解析 SQL
在正式执行 SQL 查询语句之前, MySQL 会先对 SQL 语句做解析,这个工作交由「解析器」来完成。
解析器会做如下两件事情。
1,词法分析。MySQL 会根据你输入的字符串识别出关键字,比如表名、字段名、where条件这些,并且构建出 SQL 语法树。
2,语法分析。也就是检验我们输入的 SQL 语句语法是否正确,比如把 from 写成了 form,这时 MySQL 解析器就会报错。
第四步:执行SQL
SELECT 查询语句流程主要可以分为三个阶段:
1、预处理阶段;
检查 SQL 查询语句中的表或者字段是否存在;把 select* 中的 * 符号,扩展为表上的所有列;
2、优化阶段;
优化器主要负责将 SQL 查询语句的执行方案确定下来,比如在表里面有多个索引的时候,优化器会基于查询成本的考虑,来决定选择使用哪个索引。
3、执行阶段;
根据执行计划执行 SQL 查询语句,从存储引擎读取记录,返回给客户端;
MySQL数据存放电脑位置
默认是在 /var/lib/mysql/
目录里,一个xx数据库目录下有三个文件
db.opt, 用来存储当前数据库的默认字符集和字符校验规则。
xx.frm,用来存储表结构,每建立一张表都会生成一个.frm 文件。
xx.ibd,用来存储表数据,每一张表的数据都存放在一个独立的 .ibd 文件。
ibd文件结构
表空间由行、页、区、段组成。
1、行
数据库表中的记录都是按行进行存放的,每行记录根据不同的行格式,有不同的存储结构。
2、页
虽然记录是按照行来存储的,但是数据库的读取并不以行为单位,因为如果每次只能处理一行数据,效率会非常低。所以,InnoDB 的数据是以「页」为单位来读写的。每当需要读一条记录的时候,会把包含这条记录的整个页从磁盘读入内存。
页是 InnoDB 存储引擎磁盘管理的最小单元,它默认的大小是16KB,所以数据库每次读写都是以 16KB 为单位的。
3、区
4、段
行溢出是什么
MySQL 中磁盘和内存交互的基本单位是页,一个页的大小一般是 16KB,也就是 16384字节,而一个 varchar(n) 类型的列最多可以存储 65532字节,一些大对象如 TEXT、BLOB 可能存储更多的数据,这时一个页可能就存不了一条记录。这个时候就会发生行溢出,多的数据就会存到另外的「溢出页」中。
MySQL行记录存储格式
InnoDB 提供了 4 种行格式,分别是 Redundant、Compact、Dynamic和 Compressed 行格式。
1、Redundant
Redundant是MySQL5.0之前使用的格式,现在已经废弃了。
2、Compact
这个比较经典,后面的Dynamic和Compressed 都是基于Compact改造而来的。
在compact行格式下,一行记录分为两个部分:额外信息和真实数据。
额外信息
额外信息包含 3 个部分:变长字段的长度列表、NULL 值列表、记录头信息
变长字段的长度列表主要是存储变长字段所占用的数据大小,然后读取的时候可以根据这个长度列表来读取对应的数据长度。它在存储时是按照列的逆序来存放的。之所以要逆序,是因为记录头信息中指向下一个记录的指针,指向的是下一条记录的「记录头信息」和「真实数据」之间的位置,这样的好处是向左读就是记录头信息,向右读就是真实数据,比较方便。
如果表里没有变长字段,那行格式就不会有变长字段长度列表。
NULL 值列表:因为表中的某些列可能会存储 NULL 值,如果把这些 NULL 值都放到记录的真实数据中会比较浪费空间,所以 Compact 行格式把这些值为 NULL 的列存储到 NULL值列表中。如果列是NULL,那就给它对应值为1的二进制位。如果列值不是NULL,就给它一个0的二进制位。存放时也是逆序存放。
当数据表的字段都定义成 NOT NULL 的时候,这时候表里的行格式就不会有 NULL 值列表了。
记录头信息:包含比较多的东西,比如delete_mask 标识这条数据是否被删除。next_record下一条记录的位置等等。
真实数据
记录真实数据部分除了我们定义的字段,还有三个隐藏字段,分别为:row_id、trx_id、roll_pointer。
如果表里既没有指定主键,又没有唯一约束,那么 InnoDB 就会为记录添加 row_id 隐藏字段,占六个字节。
trx_id是事务id,表示这个数据是由哪个事务生成的,占6个字节。
roll_pointer,这条记录上一个版本的指针。roll_pointer 是必需的,占 7 个字节。
Dynamic
Compressed 和 Dynamic 这两个行格式和 Compact 非常类似,主要的区别在于处理行溢出数据时有些区别。
Compact 在行溢出时,会在记录的真实数据处只会保存该列的一部分数据,而把剩余的数据放在「溢出页」中,然后真实数据处用 20 字节存储指向溢出页的地址,从而可以找到剩余数据所在的页。
而Compressed 和 Dynamic 在行溢出时,记录的真实数据处只存储 20 个字节的指针来指向溢出页,而实际的数据全都存储在溢出页中。