这时候我们各种百度谷歌,再加上自己的理解,终于弄明白了整个过程一个MySQL查询。首先,当用户想要查询订单数据时,首先会发送一个查询请求,如下图所示:可以看出,当用户发送查询请求时,因为外卖订单商品部署在Tomcat中,Tomcat中的线程首先会接收用户的请求,然后将请求交给外卖订单项。对于外卖订单项目,会根据代码在数据库中查询订单数据。无论是使用原生的JDBC,还是Mybatis、Hibernate等框架,其实底层都是先获取一个JDBC连接。通过JDBC连接,我们可以与数据库建立连接,然后通过JDBC连接执行MYSQL数据库中的sql语句。下面我们来看看如何在MYSQL数据库中执行一条sql语句,如下图所示:JDBC连接负责与MYSQL通信,会将sql语句发送给MYSQL执行。可以看到在MYSQL中,也会有线程去获取JDBC连接中的sql语句,然后执行。线程会将sql语句传递给MYSQL中的sql接口,sql接口会转发给sql解析器进行解析。例如,sql解析器会把一条sql语句中的表名、WHERE关键字后的查询条件、具体查询哪些字段等信息解析出来,然后将解析出的信息交给查询优化器进行查询加工。查询优化器会根据sql解析器解析出的sql语句信息,选择一个最高效的处理计划作为执行sql语句的执行计划,然后交给执行器执行。执行者会调用MYSQL存储引擎。这里我们以InnoDB存储引擎为例。InnoDB存储引擎中的接口会执行SQL语句,如下图所示:可以看到,MYSQLInnoDB存储引擎中有一块内存区域,叫做Buffer。池,也叫缓冲池,一般来说,MYSQL的数据是存放在磁盘上的,如果查询数据,数据会从磁盘加载到MYSQL内存中,也就是放在缓冲池BufferPool中。而BufferPool,如果再细分的话,可以看到它是由多个chunk组成的,每个chunk大约占用128MB内存,每个chunk有多个缓存页,每个缓存页大小为16kb,缓存页为用于存储加载到内存中的数据。每个缓存页都有对应的描述数据块。描述数据可以理解为指向缓存页的指针,通过描述数据可以找到对应的缓存页。此时InnoDB存储引擎执行sql语句查询数据时,首先要从磁盘加载数据,如下图所示:这里的表空间是指独立的表空间。在MySQL中,表空间分为共享表空间和独立表空间两种,但MySQL5.6.6及以后版本默认使用独立表空间。说白了,一个独立的表空间会对应磁盘上的一个表空间文件,一个表空间文件存放的是MYSQL数据库中一个表的数据。表空间中有很多数据区组,每个数据区组包含256个数据区,每个数据区包含64个数据页,因为每个数据页的默认大小是16KB,所以也就是说一个数据的大小面积为1MB。从磁盘加载数据到MYSQL内存,其实就是将数据页中的数据通过磁盘IO加载到缓冲池BufferPool中的缓存页中,然后通过InnoDB存储引擎和sql接口。那么,在整个查询过程中,哪个环节最容易出现延迟呢?答案是磁盘IO,即把磁盘中的数据页数据读到BufferPool的缓存页中的过程。那么,磁盘IO为什么会滞后呢?磁盘IO的过程是什么样的?接下来,有必要看一下这一段内容。揭开查询慢的深层次原因:在磁盘IO过程中,首先看一下磁盘的物理结构,如下图所示:写磁头、传动轴和传动臂,将数据存储在磁盘盘片上,磁盘盘片被分成无数个小扇区,每个扇区有许多不同半径的圆形轨道,不同的数据存储在不同的轨道上。实际读写数据时,主轴带动磁盘转动,然后通过传动臂的伸出,读写头在磁盘扇区的磁道上读写数据。一次磁盘IO花费的时间主要由寻道时间、旋转延迟和数据传输时间三部分组成。接下来我们看看这三个部分的耗时情况。1寻道时间我们刚刚了解到,磁盘表面被分成了无数个小扇区,每个扇区都有许多不同半径的磁道,不同的数据放在不同的磁道上。寻道时间是指将读写头移动到正确半径的磁道所需的时间。寻道时间越短,磁盘IO操作越快。目前磁盘的平均寻道时间一般为3~15ms。主流盘一般在5ms以下。2旋转延时寻道结束后,读写头需要旋转到磁道的正确位置进行数据读写,旋转延时是指寻道时间结束到寻道结束的时间读写头在这个时间间隔内旋转到磁道的正确位置。但是,我们一般使用磁盘旋转周期值的一半作为旋转延迟的近似值;常见的磁盘转速有5400转和7200转,也就是说它们每分钟可以旋转5400转和7200转。举个例子,我们以7200转为例,也就是说一秒可以转120圈,盘的旋转周期是1/120秒。因此,旋转延迟的近似值为1/120/2=4.17ms。3数据传输时间传输时间是指从磁盘读取数据或向磁盘写入数据的时间,一般在零点几毫秒以内,与前两种时间相比几乎可以忽略不计。这样,访问一个磁盘就是一次磁盘IO时间大约等于5ms+4.17ms=9ms。磁盘的顺序读写和随机读写另外,磁盘的数据读写可以分为随机读写和顺序读写两种。顺序读写,顾名思义,从磁盘中的某个位置开始读写磁头,依次读写磁盘中的数据,速度相当快,比如MYSQLredologlog,bingloglog等日志信息,例如,顺序写入数据时,会在一个大日志文件的末尾依次添加日志信息。随机读写时,读写头会随机切换,在磁盘盘片中不同半径的磁道上读写数据。频繁切换曲目的过程非常耗时。所以随机读写的速度要比顺序读写慢很多,而MYSQL从磁盘读写数据,正好是随机读写比较耗时。正是因为从MYSQL查询数据往往需要多次耗时的随机IO。所以对于一些查询效率要求比较高的数据,我们一般会选择固态硬盘来存储。固态硬盘的工作原理简单来说就是通过电子的运动来读写数据。与磁盘的物理机械运行相比,速度要快很多,但是固态硬盘的价格更高,基于成本的考虑,一般公司的机器大部分还是会选择普通的机械磁盘。磁盘IO有多慢?回到刚才,我们已经知道了磁盘IO的工作原理。我们也做了一个简单的计算。一次磁盘IO大概9ms,看着还可以,但是9ms已经很慢了。有多慢?我们可以用内存速度来比较。一般内存随机读的速度在100ns以内,1ms=1000000ns。可以看出磁盘IO需要毫秒级,而内存需要纳秒级。9ms=9*1000000ns/100ns=90000说白了,磁盘的速度比内存慢90000倍左右,那为什么从内存读写数据这么快呢?简单的说,内存其实是由CPU控制的,而且相对于磁盘的机械速度,CPU的时钟频率的速度可以说是非常快的。当用户发起查询请求时,磁盘IO一般是不确定的。具体磁盘IO次数取决于B+树的高度和当时使用的索引。极端情况下,比如没有使用索引,一次查询可能会出现100多个磁盘IO。这时候磁盘IO需要的总时间大概是9ms*100=900ms,也就是0.9秒,差不多秒级了。随着数据的快速增长,比如上亿级的数据,需要的磁盘IO数量会大幅度增加。这时候一个查询需要的时间会达到几秒。用户查询请求慢的根本原因现在,我们知道用户查询请求慢的根本原因了吗?其实说白了就是随着数据表的数据量越来越大,磁盘IO的次数也随之增加。如果我们能把磁盘IO的次数降低到一个恒定的水平,那么查询速度是非常快的,所以后面的优化都是为了降低磁盘IO的次数。
