作为一个程序员,最不能避免的就是与sql打交道,那么,在我们平时写的那么多sql它们是怎么执行,并给我们返回数据的?

比如最简单的一个查询:

select * from user where id=10;

sql简单,但问题是你知道它是如何执行的吗?

先从整体看一下mysql数据库的执行构造:

mysql 使用查询结果再次查询 mysql一次查询的过程_链表

1)server层:

所有跨存储引擎的功能都在这一层,比如:存储过程、触发器、视图等。

2)存储引擎层

其架构模式是插件的形式,比如,innodb。

以上可以认为是从宏观上,查看了mysql各个部分的流程执行顺序。

那么从微观上看的话,一条查询语句它是怎么获取到数据的呢?

知识储备:

一、数据页

在操作系统的概念中,我们从磁盘读取数据时,操作系统每次会读取4KB的数据。也就是说,即使你读取1KB的数据,操作系统也会返回4Kb的数据,先称其为页。其目的就是为了减少IO的交互。为什么它能做到这一点?

举例说明一下:

假如我们磁盘中有10条数据,并且每一条数据都是1KB,并且每一条都有一个唯一的递增的id,假如现在,我要读取id=3的这一条数据,来看一下有什么不同。

1)不使用页

我们从id=1的开始读取,当我们读取到想要的数据时,我们需要读取3次,进行了3次磁盘IO。

2)使用页

我们从id=1开始读取,然后读取一次,不符合,但是系统使用了页,返回了四条数据,其中就包含了id=3的这条数据。这就只进行了一次磁盘io,效率是不是提高了。

那么接下来,回到mysql中。innodb中也有这个页的概念,但是它的默认大小是16KB。其作用是一样的,减少访问磁盘的次数。

mysql 使用查询结果再次查询 mysql一次查询的过程_mysql_02

先看一下数据页的结构,这只是简化了的结构,其内部结构比这要复杂。只用于帮助理解。

数据在一页中是以单链表的形式存储的。而其中的slot,占两个字节,记录着相对页初始位置的偏移量。每一个slot可以 包含4到8条数据,有它辅助,可以实现二分查找,找到对应的数据页后进行遍历读取数据。

扩展:

innodb引擎的逻辑存储结构从小到大可以分为:

行、页、区、段、表空间。

其中区固定为1M的大小。

二、Buffer  Pool

bufferPool使用来做什么的?

由上面我们知道,数据库读取的时候都会读取整个页到内存,那么内存如何存储这些页,存放到那里面?

这就要使用buffer pool了。那么它做了什么?

mysql 使用查询结果再次查询 mysql一次查询的过程_链表_03

当mysql启动时,BufferPool中就会被初始化为上图结构。

其中双向链表为free链表,记录着空闲的数据页,如果此时开始的sql语句开始访问,那么就会去磁盘加载数据页到内存,将缓存页填充,然后将其对应的free链表的位置删除。

那如何知道查询的数据页在不在BufferPool中呢?

它其中还维护了一张hash表数据结构,用“表空间号+数据页号”作为key,缓存页的地址作为value,这样查询一下就知道在缓存中存不存在这个数据页了。

三、索引

innodb所以使用的是B+树。

mysql 使用查询结果再次查询 mysql一次查询的过程_数据_04

上图是一个主键索引数。

有了以上的数据储备,我们来看看开始的时候,一条查询语句是如何获取数据的。

1)通过索引,查询到对应的数据。

2)遍历双向链表(页是一个双向链表),找到数据所对应的页。

3)将数据对应的页加载到BufferPool中。

4)获取对应的数据信息返回。