mysql架构
1.总体概述
Mysql简单来说可以分成两层:
- server层
- 执行引擎层
server层:负责与客户端建立连接,分析、优化、执行sql语句
存储引擎层:负责数据的存储与读取
简单来理解就是,server层是大脑,通过分析sql语句让存储引擎层(执行者)去做存储或读取数据
2. 核心组件
2.1 server层
连接器
首先我们使用mysql第一步就是与mysql服务器建立连接1
mysql -h<ip> -u<user> -p
这一步就是建立服务器连接,mysql连接是tcp形式的(长连接/连接池优化)
连接池:一种池化思想,通过预先创建连接,避免反复创建连接产生的握手开销
上面提到了长连接优化,但是长连接也有它的弊端,mysql在断开连接的时候才会释放连接的内存资源,因此长连接可能导致占用了大量的内存资源:- 定期断开长连接
- mysql5.7后当客户端执行了一个很大的操作后,会调用reset函数重置连接
查询缓存
当连接建立完成之后,客户端就可以发送sql语句进行查询了,如果sql是select语句(查询),那么就首先会查询缓存,这查询缓存是以key-value的形式存储在内存中的,key为sql查询语句,value为sql查询的结果。但实际上,这个缓存挺没有用的(命中率低), 因此在mysql8.0后就舍弃了查询缓存,注意这里指的是server层中的缓存,执行引擎中还有bufferpool作为缓存解析器
进行词法分析和语法分析执行sql
该阶段分为三个部份:
- 预处理
- 优化
- 执行
预处理阶段主要做了两件工作:
- 检查sql语句中的字段是否存在
- 将select * 中的 * 字段,拓展为表上的所有列
优化器
这里详细介绍一下优化器:
Mysql的优化器是优化sql查询的核心组件,本质上是寻找最优解的过程,具体来说就是将客户端的sql语句,转化成最佳的物理执行逻辑
作用- 选择最佳执行路径:在多种可能的查询方式中选择最优解(全表扫描、索引扫描等)
- 生成执行计划:将sql语句转化为具体的物理执行计划
- 动态适应数据变化:根据表的统计信息动态调整执行计划
优化器类型 - 基于规则的优化器(已弃用)
- 基于代价的优化器
核心公式:cost = cpu + io 也就是考虑读取数据和读取数据行的成本
通过表统计信息,估算扫描行数,索引选择性
支持查询重写和多表连接顺序优化 - 优化器关键优化策略
- 索引选择
- 连接优化
- 子查询优化
- 查询重写
- 排序优化
- 统计信息与直方图
- 表统计信息
- 索引统计信息
- 直方图
关于优化器的部份可以看我做的simple-db优化部份
索引下推
索引下推可以减少二级索引回表的开销,提高查询的效率,具体来说他将Server层做的判断下放到执行引擎层执行1
2
3设想一个查询语句:
select * where age > 20 and sex = '男'
对于这么一个表,我们在age,sex上建立了二级索引,没有索引下推时,我们需要根据age的索引查到记录,然后进行回表,最后到server层中判断sex是否为男,在有索引下推时,可以将sex = ‘男’提前到执行引擎层判断,在找到对应的索引时检查直接检查sex而不是先回表,这样减少了回表的开销
2.2 执行引擎层
在本节我们讨论数据库中数据的存放
我们知道数据库中的数据是存放在磁盘的,但是具体是怎么存放的呢?
- db.opt 用来存放当前数据库的默认字符集和字符校验规则
- .frm 用来存放表的结构等元数据信息
- .ibd 用来存放表数据,表数据既可以存放在共享表空间中(ibdata1),也可以存放在独占表空间中(表名.ibd) mysql5.7以后都是默认存放在独占表空间中的
2.2.1 表空间结构
表空间由段、区、页、行组成的:
一个表空间由多个段组成,每个段中包含多个区,一个区中有多个页,一个页中有多个行
行
mysql提供了两种数据格式:- compact
- dynamic
一条完整的记录包括:记录的额外信息和真实数据 - 记录的额外信息:
变长字段信息:比如varchar(),记录变长字段的字节数,这里是逆序存放的 - null值列表
因为将值为null的数据存放比较浪费空间,这里通过位数据来记录空值的位置,同样是逆序的,通常给字段设置not null节省空间 - 记录头信息:
delete_mask: 标识此条数据已被删除,因此delete不是真正删除
next_record: 下一条记录的位置
record_type: 记录的标识 - 记录的真实数据:
除了字段数据之外还存在三个字段:row_id, trx_id, roll_pointer
如果有blobs、text这种字段我们知道会有很大内容的存储,假如超过了一行记录的字节限制(65535)怎么办,这时就有溢出页用于存储,如compact会记录部份数据,然后用20B记录溢出页地址,dynamic则是直接记录溢出页地址
页
数据库的记录是一行一行的,但是数据库的最小操作单元是以页为单位的,也就是说读取一行数据,是将该行所在的数据页读取出来,同时写入数据时也是以页为单位的,一次最小将16kb大小的缓存页刷入磁盘。区
我们知道InnoDB是存储引擎是通过B+树存储数据的。我们知道B+树的每一层,都是通过双向链表连接的,假如以页为单位分配空间,那么链表中的两个页之间的物理内存并不是连续的,可能离得比较远,那么磁盘查询时就会有大量的随机IO,因此在表中数据量比较大的情况下,为某个索引分配空间时,就不再按照以页为单位分配,而是按照区的方式,将物理空间上相连的页组织成一个区,这样就是顺序IO了段
是由各个区组成的,一般分为:数据段、索引段、回滚段
- 索引段:存放非叶子阶段的区的集合
- 数据段:存放叶子阶段的区的集合
- 回滚段:存放的是回滚的数据