Mysql数据库性能优化之查询性能优化
一、前言:为啥查询速度会变慢?
通常来说,查询的生命周期大致分为从客户端、到服务器,然后在服务器上进行解析,生成执行计划,执行,并返回结果给客户端。其中执行可以说是最重要的阶段,这其中包括了大量为了检索数据到存储引擎的调用以及调用后的数据处理,包括排序和分组等。在每一个消耗大量时间的查询案例中,我们都能看到一些不必要的操作、某些操作被额外重复执行了很多次、某些操作执行的太慢。优化查询的目的就是减少和消除这些操作所花费的时间。
二、优化数据访问
可以通过两个步骤分析低效的查询
1、确认应用程序是否检索大量超过需要的数据。这通常意味着访问了太多的行,但有时候也可能是访问了太多的列。
2、确认Mysql服务器层是否在分析大量超过需要的数据行。
2.1是否向数据库请求了不需要的数据
- 只查询需要的记录
mysql会查询全部的结果集,客户端的应用程序会接受全部的结果集数据,然后抛弃大部分数据。最简单的做法就是在查询后面加上limit;
- 避免在多表关联时返回全部列
在编写关联查询是只返回你需要的列
- 不要总是取出全部列
当使用select * 是要保持怀疑的眼光。这会为服务器带来额外的I/O、内存和CPU的消耗。
- 避免重复查询形同的数据
如果不小心会执行相同的查询,比如在用户评论时需要查询用户头像的URL,那么用户多次评论的时候,反复查询这个数据。比较好的方案是,当初查询的时候将这个数据缓存起来,需要的时候从缓存中取出,这样性能会更好。
2.2MySql是否在扫描额外的记录
最简单的衡量查询开销的三个指标为:响应时间、扫描的行数、返回的行数;这三个指标都会记录到Mysql的慢查询日志中,所以检查慢日志记录是找出扫描行数过多的查询的好办法。
- 响应时间
响应时间是服务时间和排队时间之和。服务时间是数据库处理这个查询真正花了多长时间。排队时间是指服务器因为等待某些资源而没有真正执行查询的时间——可以是I/O操作,可能是等待行锁。
- 扫描的行数和返回的行数
。这在一定程度上能够说明该查询找到需要的数据的效率高不高,理想情况下、扫描的行数和返回的行数应该是相同的。
- 扫描的行数和访问类型
在评估查询开销的时候,需要考虑一下从表中找到某一行数据的成本。
在explain语句中的type列反应了访问类型。索引让mysql以最高效、扫描行数最少的方式找到需要的记录。让我们来看看一个案例 select * from film_actor where film_id = 1;
这个查询返回10行数据,通过explain的结果可以看到。mysql在索引idx_fk_film_id上使用了ref访问类型来执行查询:
msyql>explain select * from film_actor where film_id = 1
id:1
type:ref
key:idx_fk_film_id
key_len:2
rows:10
extra:
explain的结果显示需要访问10行数据。查询优化器认为这中访问类型可以高效的完成查询。当我们删除对应的所有运行这个查询:
mysql> alter table film_actor drop foreign key fk_film_actor_film;
mysql>alter table film_actor drop key idx_fk_film_id;
mysql>explain select * from film_actor where film_id = 1
id:1
type:all
key:null
key_len:null
extra:using where
rows:5073
此时访问类型变成了一个全表扫描(all),现在Mysql需要通过扫描5073条记录来完成这个查询。这里的using where 表示msyql需要通过where条件来筛选存储引擎返回的记录
一般Mysql能够通过使用如下三种方式应用where条件。从好到坏依次为:
- 在索引中通过使用where条件来过滤不匹配的记录。这是在存储引擎层完成的。
- 使用索引覆盖扫描(在extra列出现了using index)来返回记录,直接从索引中过滤不需要的记录并返回命中的记录。
- 从数据表中返回数据,然后过滤不满足条件的记录(在extra列中出现using where)。在mysql服务层完成。mysql需要先从数据表读出记录然后过滤。
如果发现查询需要扫描大量的数据但只返回少数的行,那么通常可以尝试下面的技巧去优化它:- 使用索引覆盖扫描,把所有需要用到的列都放到索引中,这样存储引擎无需回表获取对应行就可以返回结果了
- 改变库表结构。
- 重写这个复杂的查询,让mysql优化器能够以更优化的方式执行这个查询。
201页