引入
之前一直没有很详细的讨论这个问题,只是将问题的解决方法归于Explain方法和慢查询日志,这里需要详细的讨论下SQL是如何慢下来的。
SQL变慢,可以大致上分为偶尔执行很慢和一直很慢,后一种才是上述解决方法针对的情况,而偶尔很慢实际上可能有很多原因导致。
所以这里分类讨论下两种慢查询的原因。
SQL偶尔执行很慢
一条 SQL 大多数情况正常,偶尔才能出现很慢的情况,针对这种情况,我觉得这条SQL语句的书写本身可能是没什么问题的,而是其他原因导致的,那会是什么原因呢?
数据库在刷新脏页
当我们要往数据库插入一条数据、或者要更新一条数据的时候,我们知道数据库会在内存中把对应字段的数据更新了,但是更新之后,这些更新的字段并不会马上同步持久化到磁盘中去,而是把这些更新的记录写入到 redo log 日记中去,等到空闲的时候,在通过 redo log 里的日记把最新的数据同步到磁盘中去。
实际上,redo log容量是有限的。那么,如果数据库很忙、更新很频繁的情况下,如果redo log写满了,这个时候就没办法等到空闲的时候再把数据同步到磁盘的,只能暂停其他操作,全身心来把数据同步到磁盘中去的,而这个时候,就会导致我们平时正常的SQL语句突然执行的很慢,所以说,数据库在在同步数据到磁盘的时候,就有可能导致我们的SQL语句执行的很慢了。
数据库redo log日志太小,默认单个文件只有50M,3个日志文件循环写,日志的切换也需要花费时间。
数据库缓存过期了
数据库缓存击穿:可能在某一时刻,缓存中的一些重要条目过期,导致大量请求落到MySQL以重新生成缓存条目。
另外,在内部删除缓存算法的效率太低时,MySQL查询缓存有时导致服务由短暂的停顿。
数据库上锁了
我们要执行的这条语句,刚好这条语句涉及到的表,别人在用,并且加锁了,我们拿不到锁,只能慢慢等待别人释放锁了。或者,表没有加锁,但要使用到的某个一行被加锁了,这个时候,我也没办法啊。
如果要判断是否真的在等待锁,我们可以用 show processlist这个命令来查看当前的状态。
针对这个问题,也可以理解成为程序设计出了问题,也可能导致SQL语句长期慢查询。
其他原因
其他原因发生的场景比较特殊,这里只做一点简单介绍。
比如:
- 从运行得很慢得外部服务来获取数据
- DNS查询偶尔会有超时现象
- 网络速度慢
- 内存不足
SQL一直执行很慢
如果这条 SQL 语句每次都执行的这么慢,那就就要好好考虑下你的 SQL 书写了,下面我们来分析下哪些原因会导致我们的 SQL 语句执行的很不理想。
没有用到索引或者索引失效
- 字段没有索引
刚好你的 c 字段上没有索引,那么抱歉,只能走全表扫描了,你就体验不会索引带来的乐趣了,所以,这回导致这条查询语句很慢。 - 字段有索引,但却没有用索引。
- 出现了运算(不能是表达式的一部分)或者使用了函数。
- 数据出现了隐式转换
- 复合索引的情况下,查询条件不满足索引最左的原则
- 负向查询(not , not in, not like, <>, != ,!>,!< ) 不会使用索引
- Mysql估计使用索引比全表扫描慢
- 以%开头的LIKE查询不能够利用B-tree索引
没有用到索引的情况很好想到,那么有没有其他情况导致变慢呢?
查询出的数据量过大
针对这一点,可以理解为:
- 查询出来的行很多,可以采用多次查询,其他的方法降低数据量。
- 查询出来的列很多,比如返回了不必要的行和列。
硬件问题
比如说IO吞吐量的问题,或者说之前说的内存问题。