阅读 106

微盟面试:为什么一条SQL语句执行很慢?

  笔者在之前的一次面试经历中,被面试官问到这一问题,当时答得不是很好,后来也查看了很多资料,今天做一个详细的总结。

      在我们的日常开发中,肯定或多或少的遇到过 SQL 语句查询慢的问题,遇到这种问题无非就两种情况:一、大部分情况下都是执行的很快,偶尔需要很长时间。二、SQL 语句一直都执行得很慢。接下来将对这两种情况进行详细分析。

一、偶尔执行很慢

      大部分情况下执行都是正常得,部分时间执行很慢,首先可能确认 SQL 语句本身是没有多大的问题的,那是什么原因呢?

1、数据库在刷新脏页

      脏页 是指当内存中的数据页和磁盘数据页中的不一致时,我们把内存数据页称作“脏页”;否则称之为“干净页”。
      当我们对数据库进行更新操作(增、删、改等)时,数据库首先会在内存中对数据进行更新,但这并不会直接同步到磁盘中,而是把这些更新的记录写道 redo log 日志中去,然后等系统空闲时再写到磁盘中。

刷新脏页的情况:

  • redo log写满了,和其他容器一样,redo log 的容量也是有限的,当我们一直对数据库进行操作,redo log 很快就会被写满,这时数据库就不能等到系统空闲时刷新脏页了,就只能暂停其他操作,尽快把数据同步到磁盘当中,因此,有时候我们的 SQL 语句执行的会比较慢的情况

  • 内存不够用了,上面介绍了,数据库会在内存中进行操作,当我们一次性查询很多数据,而这些数据都不在内存当中,更可恶的是此时的内存不够用了,这时候就需要释放一部分内存数据页,如果这些数据页都是脏页,那么将刷新脏页;如果是干净页的话直接释放就好了。

  • MySQL 认为系统空闲,这时候就会刷新脏页了。

  • MySQL 正常关闭,当数据库关闭的时候,会把脏页刷新到磁盘中,在下一次启动时,直接从磁盘中读取数据。

2、拿不到锁

      这个应该很好理解吧,数据库有各种锁,当我们执行一条语句的时候,可能会需要某种锁,但此时有其他用户在使用这个锁,那很无奈啊,只能等别人用完释放锁了。

二、一直执行很慢

      在数据量一定的情况下,如果你的 SQL 语句执行的很慢,你就要好好想想你的 SQL 语句了。
      首先我们建如下的表,为后续的说明打基础。

CREATE TABLE `test` (
        `id` int(11) NOT NULL,
        `c` int(11) DEFAULT NULL,
        `d` int(11) DEFAULT NULL,
         PRIMARY KEY(`id`)) ENGINE = InnoDB;

1、没有用到索引

 SELECT * FROM `test` WHERE `c` < 10000;
 SELECT * FROM `test` WHERE `id`-1 = 100;

      以上的这两种情况都不会用到索引,第一个是因为字段c本身没有索引,第二是在索引列做了运算操作。至于其他的一些会造成索引失效的情况,在这里就不详细介绍了。因为接下来的内容才是重点

2、数据库自己选错了索引

      数据库在执行语句的时候,会进行一个预测:走索引扫描的行数少还是直接全表扫描的行数少?扫描行数越少也好,扫描行数越少,I/O操作的次数也就越少。
      主键索引存放的值是整行字段的数据,而非主键索引上存放的值不是整段的数据,而是主键字段的值。如果查询走非主键字段,会先查询对应主键的值,然后再走一遍主键索引,然后查询到整行数据。

系统怎么预测判断呢?

      系统是通过索引的\color{#FF3030}{区分度}来判断的,一个索引上不同的值越多,意味着出现相同数值的索引越少,意味着索引的区分度越高。我们也把区分度称之为 基数 ,即 区分度越高,基数越大

      系统怎么获取这个索引的基数呢? 通过采样的办法,遍历部分数据。采样的方式可能会出现失误,一个索引的基数实际上可能很大,但在采样的过程中,很不幸,数据库遍历了那份基数很小的。然后就直接全表扫描了。



作者:CODE_WEN
链接:https://www.jianshu.com/p/4042b0753d6c


文章分类
后端
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐