前言

天天和数据库打交道,一天能写上几十条SQL语句,但你知道我们的系统是如何和数据库交互的吗?MySQL如何帮我们存储数据、又是如何帮我们管理事务?…是不是感觉真的除了写几个「select * from dual」外基本脑子一片空白?
这篇文章就将带你走进MySQL的世界,让你彻底了解系统到底是如何和MySQL交互的,MySQL在接受到我们发送的SQL语句时又分别做了哪些事情。

MySQL驱动

我们的系统在和MySQL数据库进行通信的时候,总不可能是平白无故的就能接收和发送请求,就算是你没有做什么操作,那总该是有其他的“人”帮我们做了一些事情,基本上使用过MySQL数据库的程序员多多少少都会知道MySQL驱动这个概念的。就是这个MySQL驱动在底层帮我们做了对数据库的连接,只有建立了连接了,才能够有后面的交互。

看下图表示:

这样的话,在系统和MySQL进行交互之前,MySQL驱动会帮我们建立好连接,然后我们只需要发送SQL语句就可以执行 CRUD了。一次SQL请求就会建立一个连接,多个请求就会建立多个连接,那么问题来了,我们系统肯定不是一个人在使用的,换句话说肯定是存在多个请求同时去争抢连接的情况。我们的web系统一般都是部署在tomcat 容器中的,而tomcat是可以并发处理多个请求的,这就会导致多个请求会去建立多个连接,然后使用完再都去关闭,这样会有什么问题呢?

如下图:

java 系统在通过MySQL驱动和MySQL数据库连接的时候是基于TCP/IP协议的,所以如果每个请求都是新建连接和销毁连接,那这样势必会造成不必要的浪费和性能的下降,也就说上面的多线程请求的时候频繁的创建和销毁连接显然是不合理的。必然会大大降低我们系统的性能,但是如果给你提供一些固定的用来连接的线程,这样是不是不需要反复的创建和销毁连接了呢?相信懂行的朋友会会心一笑,没错,说的就是数据库连接池。

数据库连接池:维护一定的连接数,方便系统获取连接,使用就去池子中获取,用完放回去就可以了,我们不需要关心连接的创建与销毁,也不需要关心线程池是怎么去维护这些连接的。

常见的数据库连接池有Druid、C3P0、DBCP,连接池实现原理在这里就不深入讨论了,采用连接池大大节省了不断创建与销毁线程的开销,这就是有名的「池化」思想,不管是线程池还是 HTTP 连接池,都能看到它的身影。

数据库连接池

到这里,我们已经知道的是我们的系统在访问MySQ 数据库的时候,建立的连接并不是每次请求都会去创建的,而是从数据库连接池中去获取,这样就解决了因为反复的创建和销毁连接而带来的性能损耗问题了。不过这里有个小问题,业务系统是并发的,而MySQL接受请求的线程呢,只有一个?

其实MySQL的架构体系中也已经提供了这样的一个池子,也是数据库连池。双方都是通过数据库连接池来管理各个连接的,这样一方面线程之前不需要是争抢连接,更重要的是不需要反复的创建的销毁连接。

至此系统和MySQL数据库之间的连接问题已经说明清楚了。那么MySQL数据库中的这些连接是怎么来处理的,又是谁来处理呢?

网络连接必须由线程来处理

对计算基础稍微有一点了解的的同学都是知道的,网络中的连接都是由线程来处理的,所谓网络连接说白了就是一次请求,每次请求都会有相应的线程去处理的。也就是说对于SQL语句的请求在MySQL中是由一个个的线程去处理的。

那这些线程会怎么去处理这些请求?会做哪些事情?

SQL接口

MySQL中处理请求的线程在获取到请求以后获取SQL语句去交给SQL接口去处理。

查询解析器

假如现在有这样的一个SQL

SELECT stuName,age,sex FROM students WHERE id=1

但是这个SQL是写给我们人看的,机器哪里知道你在说什么?这个时候解析器就上场了。他会将SQL接口传递过来的SQL语句进行解析,翻译成MySQL自己能认识的语言,至于怎么解析的就不需要再深究了,无非是自己一套相关的规则。

现在SQL已经被解析成 MySQL认识的样子的,那下一步是不是就是执行吗?理论上是这样子的,但是MySQL的强大远不止于此,他还会帮我们选择最优的查询路径。

什么叫最优查询路径?就是MySQL会按照自己认为的效率最高的方式去执行查询

具体是怎么做到的呢?这就要说到 MySQL 的查询优化器了

MySQL查询优化器

查询优化器内部具体怎么实现的我们不需要是关心,我需要知道的是MySQL会帮我去使用他自己认为的最好的方式去优化这条 SQL语句,并生成一条条的执行计划,比如你创建了多个索引,MySQL会依据成本最小原则来选择使用对应的索引,这里的成本主要包括两个方面, IO成本和CPU成本

IO成本: 即从磁盘把数据加载到内存的成本,默认情况下,读取数据页的IO成本是 1,MySQL是以页的形式读取数据的,即当用到某个数据时,并不会只读取这个数据,而会把这个数据相邻的数据也一起读到内存中,这就是有名的程序局部性原理,所以 MySQL 每次会读取一整页,一页的成本就是 1。所以IO的成本主要和页的大小有关

CPU成本:将数据读入内存后,还要检测数据是否满足条件和排序等CPU操作的成本,显然它与行数有关,默认情况下,检测记录的成本是 0.2。

MySQL优化器 会计算「IO 成本 + CPU」成本最小的那个索引来执行

优化器执行选出最优索引等步骤后,会去调用存储引擎接口,开始去执行被MySQL解析过和优化过的SQL语句

存储引擎

查询优化器会调用存储引擎的接口,去执行SQL,也就是说真正执行SQL的动作是在存储引擎中完成的。数据是被存放在内存或者是磁盘中的(存储引擎是一个非常重要的组件,后面会详细介绍)

执行器

执行器是一个非常重要的组件,因为前面那些组件的操作最终必须通过执行器去调用存储引擎接口才能被执行。执行器最终最根据一系列的执行计划去调用存储引擎的接口去完成SQL的执行

初识存储引擎

我们以一个更新的SQL语句来说明,SQL如下

UPDATE students SET stuName = '小强' WHERE id = 1

当我们系统发出这样的查询去交给 MySQL 的时候,MySQL 会按照我们上面介绍的一系列的流程最终通过执行器调用存储引擎去执行,流程图就是上面那个。在执行这个 SQL 的时候 SQL 语句对应的数据要么是在内存中,要么是在磁盘中,如果直接在磁盘中操作,那这样的随机IO读写的速度肯定让人无法接受的,所以每次在执行 SQL 的时候都会将其数据加载到内存中,这块内存就是InnoDB中一个非常重要的组件:缓冲池 Buffer Pool

Buffer Pool

Buffer Pool(缓冲池)是InnoDB存储引擎中非常重要的内存结构,顾名思义,缓冲池其实就是类似Redis一样的作用,起到一个缓存的作用,因为我们都知道MySQL的数据最终是存储在磁盘中的,如果没有这个Buffer Pool那么我们每次的数据库请求都会磁盘中查找,这样必然会存在IO操作,这肯定是无法接受的。但是有了Buffer Pool就是我们第一次在查询的时候会将查询的结果存到Buffer Pool中,这样后面再有请求的时候就会先从缓冲池中去查询,如果没有再去磁盘中查找,然后再放到Buffer Pool中,如下图

按照上面的那幅图,这条SQL语句的执行步骤大致是这样子的

1. innodb存储引擎会在缓冲池中查找id=1的这条数据是否存在

2. 发现不存在,那么就会去磁盘中加载,并将其存放在缓冲池中

3. 该条记录会被加上一个独占锁(总不能你在修改的时候别人也在修改吧,这个机制本篇文章不重点介绍,以后会专门写文章来详细讲解)

undo日志文件:记录数据被修改前的样子

undo顾名思义,就是没有做,没发生的意思。undo log就是没有发生事情(原本事情是什么)的一些日志
我们刚刚已经说了,在准备更新一条语句的时候,该条语句已经被加载到Buffer pool中了,实际上这里还有这样的操作,就是在将该条语句加载到Buffer Pool中的时候同时会往undo日志文件中插入一条日志,也就是将id=1的这条记录的原来的值记录下来。
这样做的目的是什么?
Innodb存储引擎的最大特点就是支持事务,如果本次更新失败,也就是事务提交失败,那么该事务中的所有的操作都必须回滚到执行前的样子,也就是说当事务失败的时候,也不会对原始数据有影响,看图说话

这里说句额外话,其实MySQL也是一个系统,就好比我们平时开发的 java 的功能系统一样,MySQL使用的是自己相应的语言开发出来的一套系统而已,它根据自己需要的功能去设计对应的功能,它即然能做到哪些事情,那么必然是设计者们当初这么定义或者是根据实际的场景变更演化而来的。所以大家放平心态,把MySQL当作一个系统去了解熟悉他。

到这一步,我们的执行的SQL语句已经被加载到Buffer Pool中了,然后开始更新这条语句,更新的操作实际是在Buffer Pool中执行的,那问题来了,按照我们平时开发的一套理论缓冲池中的数据和数据库中的数据不一致时候,我们就认为缓存中的数据是脏数据,那此时Buffer Pool中的数据岂不是成了脏数据?没错,目前这条数据就是脏数据,Buffer Pool中的记录是小强 数据库中的记录是旺财 ,这种情况 MySQL是怎么处理的呢,继续往下看

redo日志文件:记录数据被修改后的样子

除了从磁盘中加载文件和将操作前的记录保存到undo日志文件中,其他的操作是在内存中完成的,内存中的数据的特点就是:断电丢失。如果此时MySQL所在的服务器宕机了,那么Buffer Pool中的数据会全部丢失的。这个时候redo日志文件就需要来大显神通了

画外音:redo日志文件是InnoDB特有的,他是存储引擎级别的,不是MySQL级别的

redo记录的是数据修改之后的值,不管事务是否提交都会记录下来,例如,此时将要做的是update students set stuName=‘小强’ where id=1; 那么这条操作就会被记录到redo log buffer中,啥?怎么又出来一个redo log buffer ,很简单,MySQL为了提高效率,所以将这些操作都先放在内存中去完成,然后会在某个时机将其持久化到磁盘中。

截至目前,我们应该都熟悉了 MySQL 的执行器调用存储引擎是怎么将一条 SQL 加载到缓冲池和记录哪些日志的,流程如下:

1. 准备更新一条SQL 语句

2. MySQL(innodb)会先去缓冲池(BufferPool)中去查找这条数据,没找到就会去磁盘中查找,如果查找到就会将这条数据加载到缓冲池(BufferPool)中

3. 在加载到Buffer Pool的同时,会将这条数据的原始记录保存到undo日志文件中

4. innodb会在Buffer Pool中执行更新操作

5. 更新后的数据会记录在redo log buffer中

上面说的步骤都是在正常情况下的操作,但是程序的设计和优化并不仅是为了这些正常情况而去做的,也是为了那些临界区和极端情况下出现的问题去优化设计的

这个时候如果服务器宕机了,那么缓存中的数据还是丢失了。真烦,竟然数据总是丢失,那能不能不要放在内存中,直接保存到磁盘呢?很显然不行,因为在上面也已经介绍了,在内存中的操作目的是为了提高效率。

此时,如果MySQL真的宕机了,那么没关系的,因为MySQL会认为本次事务是失败的,所以数据依旧是更新前的样子,并不会有任何的影响。

好了,语句也更新好了那么需要将更新的值提交啊,也就是需要提交本次的事务了,因为只要事务成功提交了,才会将最后的变更保存到数据库,在提交事务前仍然会具有相关的其他操作

将redo Log Buffer中的数据持久化到磁盘中,就是将redo log buffer中的数据写入到redo log磁盘文件中,一般情况下,redo log Buffer数据写入磁盘的策略是立即刷入磁盘(具体策略情况在下面小总结出会详细介绍),上图

如果redo log Buffer刷入磁盘后,数据库服务器宕机了,那我们更新的数据怎么办?此时数据是在内存中,数据岂不是丢失了?不,这次数据就不会丢失了,因为redo log buffer中的数据已经被写入到磁盘了,已经被持久化了,就算数据库宕机了,在下次重启的时候MySQL也会将redo日志文件内容恢复到Buffer Pool中(这边我的理解是和Redis的持久化机制是差不多的,在Redis启动的时候会检查rdb或者是aof或者是两者都检查,根据持久化的文件来将数据恢复到内存中)
到此为止,从执行器开始调用存储引擎接口做了哪些事情呢?

1. 准备更新一条SQL语句

2. MySQL(innodb)会先去缓冲池(BufferPool)中去查找这条数据,没找到就会去磁盘中查找,如果查找到就会将这条数据加载

到缓冲池(BufferPool)中

3. 在加载到Buffer Pool的同时,会将这条数据的原始记录保存到undo日志文件中

4. innodb会在Buffer Pool中执行更新操作

5. 更新后的数据会记录在redo log buffer中

—到此是前面已经总结过的—

6. MySQL提交事务的时候,会将redo log buffer中的数据写入到redo日志文件中刷磁盘可以通过 innodb_flush_log_at_trx_commit参数来设置

值为0表示不刷入磁盘

值为1表示立即刷入磁盘

值为2表示先刷到os cache

7. myslq重启的时候会将redo日志恢复到缓冲池中

截止到目前为止,MySQL的执行器调用存储引擎的接口去执行【执行计划】提供的SQL的时候InnoDB做了哪些事情也就基本差不多了,但是这还没完。下面还需要介绍下MySQL级别的日志文件bin log

bin log日志文件:记录整个操作过程

上面介绍到的redo log是 InnoDB 存储引擎特有的日志文件,而bin log属于是 MySQL 级别的日志。redo log记录的东西是偏向于物理性质的,如:“对什么数据,做了什么修改”。bin log是偏向于逻辑性质的,类似于:“对 students 表中的 id 为 1 的记录做了更新操作” 两者的主要特点总结如下:

bin log文件是如何刷入磁盘的?

bin log 的刷盘是有相关的策略的,策略可以通过sync_bin log来修改,默认为0,表示先写入os cache,也就是说在提交事务的时候,数据不会直接到磁盘中,这样如果宕机bin log数据仍然会丢失。所以建议将sync_bin log设置为1表示直接将数据写入到磁盘文件中。

刷入bin log 有以下几种模式

1、 STATMENT

基于SQL语句的复制(statement-based replication, SBR),每一条会修改数据的SQL语句会记录到bin log中

【优点】:不需要记录每一行的变化,减少了 bin log 日志量,节约了 IO , 从而提高了性能
【缺点】:在某些情况下会导致主从数据不一致,比如执行sysdate()、slepp()等

2、ROW

基于行的复制(row-based replication, RBR),不记录每条SQL语句的上下文信息,仅需记录哪条数据被修改了

【优点】:不会出现某些特定情况下的存储过程、或 function、或 trigger 的调用和触发无法被正确复制的问题
【缺点】:会产生大量的日志,尤其是 alter table 的时候会让日志暴涨

3、MIXED

基于STATMENT 和 ROW两种模式的混合复制( mixed-based replication, MBR ),一般的复制使用STATEMENT模式保存bin log,对于STATEMENT模式无法复制的操作使用ROW模式保存bin log那既然bin log也是日志文件,那它是在什么记录数据的呢?

其实MySQL在提交事务的时候,不仅仅会将redo log buffer中的数据写入到redo log文件中,同时也会将本次修改的数据记录到bin log文件中,同时会将本次修改的bin log文件名和修改的内容在bin log中的位置记录到redo log中,最后还会在redo log最后写入commit标记,这样就表示本次事务被成功的提交了。

如果在数据被写入到bin log文件的时候,刚写完,数据库宕机了,数据会丢失吗?

首先可以确定的是,只要redo log最后没有 commit 标记,说明本次的事务一定是失败的。但是数据是没有丢失了,因为已经被记录到redo log的磁盘文件中了。在 MySQL 重启的时候,就会将 redo log 中的数据恢复(加载)到Buffer Pool中。

好了,到目前为止,一个更新操作我们基本介绍得差不多,但是你有没有感觉少了哪件事情还没有做?是不是你也发现这个时候被更新记录仅仅是在内存中执行的,哪怕是宕机又恢复了也仅仅是将更新后的记录加载到Buffer Pool中,这个时候MySQL 数据库中的这条记录依旧是旧值,也就是说内存中的数据在我们看来依旧是脏数据,那这个时候怎么办呢?

其实MySQL会有一个后台线程,它会在某个时机将我们Buffer Pool中的脏数据刷到MySQL数据库中,这样就将内存和数据库的数据保持统一了。

本文总结

到此,关于Buffer Pool、Redo Log Buffer 和undo log、redo log、bin log 概念以及关系就基本差不多了。
我们再回顾下

1. Buffer Pool是MySQL的一个非常重要的组件,因为针对数据库的增删改操作都是在Buffer Pool中完成的

2. Undo log记录的是数据操作前的样子

3. redo log记录的是数据被操作后的样子(redo log是Innodb存储引擎特有)

4. bin log记录的是整个操作记录(这个对于主从复制具有非常重要的意义)

从准备更新一条数据到事务的提交的流程描述

1. 首先执行器根据 MySQL 的执行计划来查询数据,先是从缓存池中查询数据,如果没有就会去数据库中查询,如果查询到了就将其放到缓存池中

2. 在数据被缓存到缓存池的同时,会写入undo log日志文件

3. 更新的动作是在 BufferPool 中完成的,同时会将更新后的数据添加到redo log buffer中

4. 完成以后就可以提交事务,在提交的同时会做以下三件事

5.(第一件事)将redo log buffer中的数据刷入到redo log文件中

6.(第二件事)将本次操作记录写入到bin log文件中

7.(第三件事)将bin log文件名字和更新内容在bin log 中的位置记录到redo log中,同时在redo log最后添加commit 标记

至此表示整个更新事务已经完成

总结

文章到这里就结束了,系统是如何和MySQL数据库打交道,提交一条更新的SQL语句到MySQL,MySQL执行了哪些流程,做了哪些事情从宏观上都已经讲解完成了。更多的Buffer Pool的细节将会在之后的文章中详解,金三银四读者福利:整理好的MySQL实战笔记,金三银四Java面试资料集锦。

一文读懂查询sql的执行流程和底层原理相关推荐

  1. 面试官:说说一条查询sql的执行流程和底层原理?

    作者:孤独烟,资深后端工程师,业内知名原创作者 一条查询SQL执行流程图如下 本文改编自<高性能Mysql>,烟哥用小说的形式来讲这个内容. 序章 自我介绍 我是一条sql,就是一条长长的 ...

  2. 一文读懂 Shiro 登录认证全流程

    一文读懂 Shiro 登录认证全流程 登录入口 执行登录 UserRealm Apache Shiro 是 Java 的一个安全框架.Shiro 可以帮助我们完成:认证.授权.加密.会话管理.与 We ...

  3. Oracle SQL语句执行流程与顺序原理详解

    以前读的文章,保存到本地了,忘记来源了,分享一下,本地存着怕丢了 Oracle SQL语句执行流程与顺序原理详解 第一步:客户端把语句发给服务器端执行 当我们在客户端执行SQL语句时,客户端会把这条S ...

  4. openGauss简单查询SQL的执行流程解析

    目录 简单查询的执行 gdb调试 上一期酷哥分析了openGauss数据库的启动过程,包括主线程,辅助线程及业务处理线程的启动过程,这一期主要分析简单查询语句在业务处理线程Postgres上的执行流程 ...

  5. 一文读懂供应链仓储管理全流程

    一.仓储网络布局 园区基本情况 一般情况下:仓库的选址规划布局,主要根据订单量的区域分布及未来增涨潜力来进行需求评估,重点考虑的几个评估维度,如下:1.订单集中区域的周边车程范围(具体视库源,业务产品 ...

  6. 前端面试必会 | 一文读懂 JavaScript 中的 this 关键字

    this 是一个令无数 JavaScript 编程者又爱又恨的知识点.它的重要性毋庸置疑,然而真正想掌握它却并非易事.希望本文可以帮助大家理解 this. JavaScript 中的 this Jav ...

  7. 一文读懂HTTP/2及HTTP/3特性

    前言 HTTP/2 相比于 HTTP/1,可以说是大幅度提高了网页的性能,只需要升级到该协议就可以减少很多之前需要做的性能优化工作,当然兼容问题以及如何优雅降级应该是国内还不普遍使用的原因之一. 虽然 ...

  8. 一文读懂无线充电技术(附方案选型及原理分析)

    一文读懂无线充电技术(附方案选型及原理分析) 标签: 无线充电 技术 电子 解决方案 2017年09月02日 10:27:12 5807人阅读 评论(1) 收藏 举报 (function () {   ...

  9. 一文读懂 MySQL Explain 执行计划

    一.前言 上周老周的一个好朋友让我出一篇教你读懂 SQL 执行计划,和我另一位读者反馈的面试题如何排查慢 SQL 的强相关,索性先出一篇一文读懂 MySQL Explain 执行计划.Explain ...

最新文章

  1. 【Linux 内核】编译 Linux 内核 ⑥ ( 安装 OpenSSL | 安装其它依赖库 | 内核编译完成 )
  2. 解决三星手机EditText背景色的问题
  3. onenetsim定位功能吗_经常玩手机的抓紧看看,原来手机键盘还隐藏4个实用功能,真实用...
  4. java sdk下载_Java Sdk下载 | 保利威帮助中心
  5. python数据可视化是什么_Python数据分析:可视化
  6. Java学习日报—泳道与Feign—2021/11/30
  7. 扩展可以支持枚举类型的DorpDownList控件
  8. Android 实现 相机、相册功能 + 图片存取数据库操作
  9. html单元格竖着排列,html表格,表头竖向固定,横向滚动的例子
  10. 一次慢查询暴露的隐蔽的问题
  11. poj 3061 子序列
  12. java购物系统需求分析_java网上购物系统需求分析报告.doc
  13. submail 发送国际短信,国内短信,国际国内邮箱工具类
  14. HTML5+CSS3笔记 (黑马pink老师)
  15. no more duplicates will be shown
  16. w10计算机管理员权限在哪里设置密码,Win10使用管理员权限需要PIN密码的设置方法 三种解决方法...
  17. SpringBoot Poi导出word,浏览器下载
  18. 基于RFID定位技术的工地人员定位考勤系统-工地人员定位-新导智能
  19. ByteBuffer使用揭秘
  20. unix操作系统(360搜索百科)介绍unix系统产生的由来和发展是重点,而百度百科中没有仔细介绍这方面

热门文章

  1. Elasticsearch从入门到放弃:瞎说Mapping
  2. 2022-3-31 Leetcode 415.字符串相加
  3. 跪了!3秒钟完成别人半天的工作量!网友:别再用Excel了!
  4. Spring源码工具类ClassUtils之isAssignable
  5. 【西安安泰】功率放大器在超声波无损检测技术中的应用
  6. HTML入门基础教程相关知识
  7. 基于android的音乐播放器毕业设计,基于android音乐播放器的毕业设计ppt
  8. 证书、根证书、CA、证书不受信任详解
  9. 如何写出更加契合浙大MBA项目提面申请资料?
  10. linux多线程求和_Linux下使用两个线程协作完成一个任务的简易实现