SQL:我为何慢你内心没数吗?

SQL 语句执行慢的缘由是面试中常常会被问到的,对于服务端开发来讲也是必需要关注的问题。mysql

在生产环境中,SQL 执行慢是很严重的事件。那么如何定位慢 SQL、慢的缘由及如何防患于未然。接下来带着这些问题让咱们开启本期之旅!面试

图注:思惟导图sql

写操做 

做为后端开发,平常操做数据库最经常使用的是写操做和读操做。读操做咱们下边会讲,这个分类里咱们主要来看看写操做时为何会致使 SQL 变慢。数据库

刷脏页

脏页的定义是这样的:内存数据页和磁盘数据页不一致时,那么称这个内存数据页为脏页。后端

那为何会出现脏页,刷脏页又怎么会致使 SQL 变慢呢?那就须要咱们来看看写操做时的流程是什么样的。缓存

对于一条写操做的 SQL 来讲,执行的过程当中涉及到写日志,内存及同步磁盘这几种状况。服务器

 

图注:Mysql 架构图架构

这里要提到一个日志文件,那就是 redo log,位于存储引擎层,用来存储物理日志。在写操做的时候,存储引擎(这里讨论的是 Innodb)会将记录写入到 redo log 中,并更新缓存,这样更新操做就算完成了。后续操做存储引擎会在适当的时候把操做记录同步到磁盘里。并发

看到这里你可能会有个疑问,redo log 不是日志文件吗,日志文件就存储在磁盘上,那写的时候岂不很慢吗?高并发

其实,写redo log 的过程是顺序写磁盘的,磁盘顺序写减小了寻道等时间,速度比随机写要快不少( 相似Kafka存储原理),所以写 redo log 速度是很快的。

好了,让咱们回到开始时候的问题,为何会出现脏页,而且脏页为何会使 SQL 变慢。你想一想,redo log 大小是必定的,且是循环写入的。在高并发场景下,redo log 很快被写满了,可是数据来不及同步到磁盘里,这时候就会产生脏页,而且还会阻塞后续的写入操做。SQL 执行天然会变慢。

写操做时 SQL 慢的另外一种状况是可能遇到了锁,这个很容易理解。举个例子,你和别人合租了一间屋子,只有一个卫生间,大家俩同时都想去,但对方比你早了一丢丢。那么此时你只能等对方出来后才能进去。

对应到 Mysql 中,当某一条 SQL 所要更改的行恰好被加了锁,那么此时只有等锁释放了后才能进行后续操做。

可是还有一种极端状况,你的室友一直占用着卫生间,那么此时你该怎么整,总不能尿裤子吧,多丢人。对应到Mysql 里就是遇到了死锁或是锁等待的状况。这时候该如何处理呢?

Mysql 中提供了查看当前锁状况的方式:

 经过在命令行执行图中的语句,能够查看当前运行的事务状况,这里介绍几个查询结果中重要的参数:

当前事务若是等待时间过长或出现死锁的状况,能够经过 「kill 线程ID」 的方式释放当前的锁。

这里的线程 ID 指表中 trx_mysql_thread_id 参数。

读操做

说完了写操做,读操做你们可能相对来讲更熟悉一些。SQL 慢致使读操做变慢的问题在工做中是常常会被涉及到的。

慢查询

在讲读操做变慢的缘由以前咱们先来看看是如何定位慢 SQL 的。Mysql 中有一个叫做慢查询日志的东西,它是用来记录超过指定时间的 SQL 语句的。默认状况下是关闭的,经过手动配置才能开启慢查询日志进行定位。

具体的配置方式是这样的:

  • 查看当前慢查询日志的开启状况:

  • 开启慢查询日志(临时):

注意这里只是临时开启了慢查询日志,若是 mysql 重启后则会失效。能够 my.cnf 中进行配置使其永久生效。

存在缘由

知道了如何查看执行慢的 SQL 了,那么咱们接着看读操做时为何会致使慢查询。

(1)未命中索引

SQL 查询慢的缘由之一是可能未命中索引,关于使用索引为何能使查询变快以及使用时的注意事项,网上已经不少了,这里就很少赘述了。

(2)脏页问题

另外一种仍是咱们上边所提到的刷脏页状况,只不过和写操做不一样的是,是在读时候进行刷脏页的。

是否是有点懵逼,别急,听我娓娓道来:

为了不每次在读写数据时访问磁盘增长 IO 开销,Innodb 存储引擎经过把相应的数据页和索引页加载到内存的缓冲池(buffer pool)中来提升读写速度。而后按照最近最少使用原则来保留缓冲池中的缓存数据。

那么当要读入的数据页不在内存中时,就须要到缓冲池中申请一个数据页,但缓冲池中数据页是必定的,当数据页达到上限时此时就须要把最久不使用的数据页从内存中淘汰掉。但若是淘汰的是脏页呢,那么就须要把脏页刷到磁盘里才能进行复用。

你看,又回到了刷脏页的状况,读操做时变慢你也能理解了吧?

防患于未然

知道了缘由,咱们如何来避免或缓解这种状况呢?

首先来看未命中索引的状况:

不知道你们有没有使用 Mysql 中 explain 的习惯,反正我是每次都会用它来查看下当前 SQL 命中索引的状况。避免其带来一些未知的隐患。

这里简单介绍下其使用方式,经过在所执行的 SQL 前加上 explain 就能够来分析当前 SQL 的执行计划:

执行后的结果对应的字段概要描述以下图所示:

这里须要重点关注如下几个字段: 

一、type

表示 MySQL 在表中找到所需行的方式。其中经常使用的类型有:ALL、index、range、 ref、eq_ref、const、system、NULL 这些类型从左到右,性能逐渐变好。

  • ALL:Mysql 遍历全表来找到匹配的行; 

  • index:与 ALL 区别为 index 类型只遍历索引树; 

  • range:只检索给定范围的行,使用一个索引来选择行; 

  • ref:表示上述表的链接匹配条件,哪些列或常量被用于查找索引列上的值; 

  • eq_ref:相似ref,区别在于使用的是否为惟一索引。对于每一个索引键值,表中只有一条记录匹配,简单来讲,就是多表链接中使用 primary key 或者 unique key做为关联条件; 

  • const、system:当 Mysql 对查询某部分进行优化,并转换为一个常量时,使用这些类型访问。如将主键置于 where 列表中,Mysql 就能将该查询转换为一个常量,system 是 const类型的特例,当查询的表只有一行的状况下,使用system; 

  • NULL:Mysql 在优化过程当中分解语句,执行时甚至不用访问表或索引,例如从一个索引列里选取最小值能够经过单独索引查找完成。

二、possible_keys 

查询时可能使用到的索引(但不必定会被使用,没有任何索引时显示为 NULL)。

三、key

实际使用到的索引。

四、rows

估算查找到对应的记录所须要的行数。

五、Extra

比较常见的是下面几种: 

  • Useing index:代表使用了覆盖索引,无需进行回表;

  • Using where:不用读取表中全部信息,仅经过索引就能够获取所需数据,这发生在对表的所有的请求列都是同一个索引的部分的时候,表示mysql服务器将在存储引擎检索行后再进行过滤; 

  • Using temporary:表示MySQL须要使用临时表来存储结果集,常见于排序和分组查询,常见 group by,order by;

  • Using filesort:当Query中包含 order by 操做,并且没法利用索引完成的排序操做称为“文件排序”。 

对于刷脏页的状况,咱们须要控制脏页的比例,不要让它常常接近 75%。同时还要控制 redo log 的写盘速度,而且经过设置 innodb_io_capacity 参数告诉 InnoDB 你的磁盘能力。

总结

写操做 

  • 当 redo log 写满时就会进行刷脏页,此时写操做也会终止,那么 SQL 执行天然就会变慢。 

  • 遇到所要修改的数据行或表加了锁时,须要等待锁释放后才能进行后续操做,SQL 执行也会变慢。

读操做

  • 读操做慢很常见的缘由是未命中索引从而致使全表扫描,能够经过 explain 方式对 SQL 语句进行分析。 
  • 另外一种缘由是在读操做时,要读入的数据页不在内存中,须要经过淘汰脏页才能申请新的数据页从而致使执行变慢。

关于做者

做者:你们好,我是莱乌,BAT搬砖工一枚。从小公司进入大厂,一路走来收获良多,想将这些经验分享给有须要的人,所以建立了公众号【IT界农民工】。定时更新,但愿能帮助到你。在公众号内回复【pdf】可免费获取一份Redis面经手册。

相关文章
相关标签/搜索