一、介绍
一条sql在mysql底层是如何执行,底层原理到底是怎么样的呢,下面我们先来介绍一些组成部分。
二、组成
1、MySQL 驱动
-
帮应用系统跟mysql数据库建立连接
-
一次 SQL 请求就会建立一个连接
-
请求协议
- TCP/IP
2、数据库连接池
建立的连接并不是每次请求都会去创建的,而是从数据库连接池中去获取,这样就解决了因为反复的创建和销毁连接而带来的性能损耗问题了。
3、查询解析器
SQL 是写给我们人看的,机器哪里知道你在说什么?这个时候解析器就上场了。他会将 SQL 接口传递过来的 SQL 语句进行解析,翻译成 MySQL 自己能认识的语言
4、查询优化器
MySQL 会帮我们去使用他自己认为的最好的方式去优化这条 SQL 语句,并生成一条条的执行计划,比如你创建了多个索引,MySQL 会依据成本最小原则来选择使用对应的索引,这里的成本主要包括两个方面, IO 成本和 CPU 成本
5、执行器
执行器最终会根据一系列的执行计划去调用存储引擎的接口去完成 SQL 的执行
6、存储引擎
真正执行 SQL 的动作是在存储引擎中完成的,通过存储引擎去缓存或者磁盘获取数据
7、Buffer Pool
-
定义
- buffer pool 是一块内存区域,为了「提高数据库的性能」,当数据库操作数据的时候,把硬盘上的数据加载到 buffer pool,不直接和硬盘打交道,操作的是 buffer pool 里面的数据,数据库的增删改查都是在 buffer pool 上进行,buffer pool 里面缓存的数据内容也是一个个数据页
-
链表
-
free 链表
- 用于帮助我们找到空闲的缓存页
-
flush 链表
- 用于找到脏缓存页,也就是需要刷盘的缓存页
-
lru 链表
- 用来淘汰不常被访问的缓存页,分为热数据区和冷数据区,冷数据区主要存放那些不常被用到的数据
-
-
预读机制
- 存储引擎的接口在被 Server 层调用时,会在响应的同时进行预判,将下次可能用到的数据和索引加载到 Buffer Pool
8、undo log
- 数据加载到 Buffer Pool 中同时会往 undo 日志文件中插入一条日志,记录数据修改前的样子
- 目的是支持事务
- 更新操作是在Buffer Pool中执行的
9、redo log
- redo日志保持了事务的持久性,确保即使在系统崩溃的情况下,只要 redo 日志还在,就可以重做事务,从而保障数据不会丢失。
- 当事务修改的脏页被刷新到磁盘上时,redo日志的主要任务就完成了。之后,这部分占用的重做日志空间可以被重用(覆盖)。
- redo 日志文件是 InnoDB 特有的,他是存储引擎级别的,不是 MySQL 级别的
10、binlog
-
定义
- 记录整个操作过程
-
刷盘策略
-
sync_bin log参数
- 默认为 0,表示先写入 os cache,也就是说在提交事务的时候,数据不会直接到磁盘中,这样如果宕机bin log数据仍然会丢失
- 1 表示直接将数据写入到磁盘文件中,建议
-
-
刷入模式
-
STATMENT
-
定义
- 基于 SQL 语句的复制,每一条会修改数据的 SQL 语句会记录到 bin log 中
-
优点
- 不需要记录每一行的变化,减少了 bin log 日志量,节约了 IO , 从而提高了性能
-
缺点
- 在某些情况下会导致主从数据不一致,比如执行sysdate()、slepp()等,实时时间不一样
-
-
ROW
-
定义
- 基于行的复制,仅需记录哪条数据被修改了
-
优点
- 不会出现某些特定情况下的存储过程、或 function、或 trigger 的调用和触发无法被正确复制的问题
-
缺点
- 会产生大量的日志,尤其是 alter table 的时候会让日志暴涨
-
-
MIXED
-
定义
- 混合模式,一般的复制使用 STATEMENT 模式保存 bin log ,对于 STATEMENT 模式无法复制的操作使用 ROW 模式保存 bin log
-
-
三、流程
-
准备更新一条 SQL 语句
-
通过mysql驱动建立连接,连接从数据库连接池获取
-
查询解析器解析sql
-
查询优化器优化sql生成执行计划
-
执行器根据一系列的执行计划去调用存储引擎的接口去完成 SQL 的执行
-
MySQL(innodb)首先会先去缓冲池(BufferPool)中去查找这条数据,没找到就会去磁盘中查找,如果查找到就会将这条数据加载到缓冲池(BufferPool)中
-
在加载到 Buffer Pool 的同时,会将这条数据的原始记录保存到 undo 日志文件中
-
innodb 会在 Buffer Pool 中执行更新操作
-
更新后的数据会记录在 redo log buffer 中
- MySQL 为了提高效率,所以将这些操作都先放在内存中去完成,然后会在某个时机将其持久化到磁盘中
-
完成以后就可以提交事务,在提交的同时会做以下三件事
-
(第一件事)将redo log buffer中的数据刷入到 redo log 文件中
-
(第二件事)将本次操作记录写入到 binlog文件中
-
(第三件事)将 binlog 文件名字和更新内容在 binlog 中的位置记录到redo log中,同时在 redo log 最后添加 commit 标记
- 如果在数据被写入到binlog文件的时候,刚写完,数据库宕机了,数据会丢失吗? 首先可以确定的是,只要redo log最后没有 commit 标记,说明本次的事务一定是失败的。但是数据是没有丢失了,因为已经被记录到redo log的磁盘文件中了。在 MySQL 重启的时候,就会将 redo log 中的数据恢复(加载)到Buffer Pool中。
-
至此表示整个更新事务已经完成
-
MySQL 会有一个后台线程,它会在某个时机将我们Buffer Pool中的脏数据刷到 MySQL 数据库中,这样就将内存和数据库的数据保持统一了