前言
本文只但从数据库本身来看查询慢的可能因素,至于内存不够、网速较慢不属于本文讨论范畴。
本文内容参考自公众号文章:
腾讯面试:一条SQL语句执行得很慢的原因有哪些?---不看后悔系列
我对公众号中的内容作自己的梳理总结
开始
首先要分类讨论一下,这条查询语句是在偶尔的情况下查询效率慢,还是一直都存在查询效率慢的问题。
对于第一种情况,可能查询语句本身没有问题,是数据库遇到了其他问题;
对于第二种情况,应该是查询语句出了问题,需要优化
偶尔效率慢的情况
原因一:刷新“脏”页
什么是“脏”页
当对数据库进行插入或者更新操作时,数据库会立刻将内存的数据页上的信息更新,但是不会立刻将将更新的数据存到磁盘上,而是先保存到redo log中,等合适的时机在将redo log的信息存储到磁盘上
针对这种内存中的数据页和磁盘上的数据不同的情况我们将内存中的数据页称为“脏”页,而内存中和磁盘上数据相同的情况则称为“干净”页。
刷新“脏”页时,系统会暂停其他的操作,全身心的将数据存到磁盘中,就会导致平常正常执行的mysql语句变慢
什么时候会刷新“脏”页
case1:redo log装满时
case2:内存不够用时
case3:mysql认为系统空闲时
case4:mysql正常关闭时
原因二:数据被锁住
可以用show processlist命令查看一下语句执行的状态,查看要查询的数据是否被锁住
一直都存在效率慢的情况
原因一:查询的数据量太大
查看是否查询了不必要的行与列,避免用select * from table这样的语句
原因二:没有用到索引
当数据量很大时,若没有用索引采用全表索引是很耗费时间的。而这里没有用到索引由可以分多钟情况
case1:没有建索引
case2:索引失效
引起索引失效的可能原因
1)在索引列上用了内置函数或者其他+-*/运算
2)用通配符开头
3)多列索引违背最佳最匹配原则
4)or操作符容器造成索引失效,除非or的每个操作列都有索引
5)字符串不加单引号
case3:系统选错索引
系统选错索引其实是索引失效的一种形式,但是由于涉及到的知识点较多,所以单独拿出来分析。
系统选错索引导致索引失效时系统将全表扫描与用索引要扫描的行数进行比较,若是觉得运用索引反而要复杂,则系统就会放弃索引采用全表扫描的方式。
那么什么时候会出现运用索引反而比全表扫描效率更低的情况呢?
首先我们都知道主键索引保存的是整行数据,而非主键索引保存的是主键的值。所以运用非主键索引时要先定位到满足条件的行的主键值,在由主键值拿到整行数据的信息,要经过两次索引的过程。
极端情况下,当索引寻找的数据条件全表都满足时,则此时索引寻找相比于全表扫描反而多了一系列索引过程。
所以系统在判断是否需要应用索引时会先判断如果运用索引大致需要扫描多少行,如果系统预测要扫描的行数很多,则系统会选择放弃索引采取全表扫描的方式。
系统如何判断运用索引需要扫描的行数?这就需要用到索引的区分度。索引的区分度又称为基数,一个索引上不同的值越多,意味着出现相同数值的索引越少,意味着索引的区分度越高。
区分度越高,则满足索引查询条件的数据就越少,则系统预测扫描的函数不多。
那么索引的区分度又是如何得来的呢?
采样。系统通过采样的方式来推测索引的区分度。既然是采样则就会有误差,如果你想避免这种误差,不想要系统进行这种它认为的人性化的选择方式,你可以强制运用索引
select * from t index(a) where c>100 and c<1000;
你也可用下面的第一行命令查看索引的基数,如果基数和实际不符合的话你也可用第二行命令让系统重新采样计算索引基数
1 show index from t; 2 analyze table t;
可以用explain+SQL查询语句来查看SQL语句的执行过程,查看是否用到了预期索引
explain命令可以参考我的另一篇博文mysql优化一之查询优化