前
总结一下出现慢查询的原因,参考《高性能Mysql》
正
原因
慢查询的具体原因很多,但是最基本的原因就是查询访问的数据太多,其中包括检索了过多的列和过多的行,或者mysql服务器层分析了大量不需要的数据
请求了不需要的数据
- 查询了不需要的记录
这里书中举例的场景是搜索大量数据,然后取出前面的N行,而不是从sql中直接limit,这个问题一般不会犯。 - 多表关联时返回全部列 && 总是取出全部列
这两个场景类似,也就是说不要select *
,取出自己需要的列就可以了。 - 查询重复的数据
一般是说业务中没有加缓存,重复执行相同的语句,返回相同的数据,这一点不是再说慢查询,是从数据库性能出发的。
Mysql是否扫描了额外的记录
这部分有三个指标
- 响应时间
- 扫描的行数
- 返回的行数
其中响应时间是指mysql执行sql的时间和等待资源(io和行锁)的时间。
理想情况下,扫描的行数应该和返回的行数相同,但是一般很难做到。这部分的解决方案就是加合适的索引。
对于一般的where条件,我们从执行计划可以看到,最好的是在索引中直接使用where条件过滤,其次是使用覆盖索引,不需要回表查询数据(using index),直接返回命中结果,最后就是从数据表中返回数据,然后过滤不满足条件的记录。
这里额外说一下Mysql的服务器层和引擎层
首先是服务层,服务层是向上对外提供查询能力,向下调用引擎读写数据。首先是连接器,接收客户端请求,建立连接,然后是查询缓存,是语句和结果的kv缓存,如果命中直接返回,接着是分析器,会对语句进行词法分析和语法分析,例如查询不存在的列就是这个阶段报错,接着是优化器,决定使用哪个引擎,或者join表的顺序,接下来就是执行器,根据语句调用引擎读写。
引擎层就是具体的innoDb这样的提供读写接口的引擎。
解决
- 拆分复杂查询为多个简单查询,
- 切分查询,一次性执行一个大的sql(删除大量数据),会锁住很多数据,耗尽系统资源,阻塞小sql,所以可以拆分,多次执行。
- 分解关联查询,在工程中,一般都不会使用join,都是在应用层代码中完成聚合逻辑,一个是使得缓存的命中变高,因为聚合多表查询条件,查询组合就会变得很多,缓存命中率就会下降,同时在应用层更容易扩展且容易复用,对于引擎层,多个小的查询也可以降低锁粒度