写在前面:这是msql性能调优的第二个部分
MySQL性能优化之查询
一 MySQL查询流程
我们该如何进行sql优化呢, 首先我们需要知道,sql优化其实主要是解决查询的优化问题,所以我们先从数据库的查询开始入手,下面这幅图显示了查询的执行路径:
① 客户端将查询发送到服务器;
② 服务器检查查询缓存,如果找到了,就从缓存中返回结果,否则进行下一步。
③ 服务器解析,预处理。
④ 查询优化器优化查询
⑤ 生成执行计划,执行引擎调用存储引擎API执行查询
⑥服务器将结果发送回客户端。 查询缓存 在解析一个查询语句之前,如果查询缓存是打开的,那么MySQL会优先检查这个查询是否命中查询缓存中的数据,如果命中缓存直接从缓存中拿到结果并返回给客户端。这种情况下,查询不会被解析,不用生成执行计划,不会被执行。
语法解析和预处理器 MySQL通过关键字将SQL语句进行解析,并生成一棵对应的“解析树”。MySQL解析器将使用MySQL语法规则验证和解析查询。
查询优化器 语法书被校验合法后由优化器转成查询计划,一条语句可以有很多种执行方式,最后返回相同的结果。优化器的作用就是找到这其中最好的执行计划。
查询执行引擎 在解析和优化阶段,MySQL将生成查询对应的执行计划,MySQL的查询执行引擎则根据这个执行计划来完成整个查询。最常使用的也是比较最多的引擎是MyISAM引擎和InnoDB引擎。mysql5.5开始的默认存储引擎已经变更为innodb了。
二 查询优化
前面的查询流程分析,我们大概了解了MySQL是如何执行的,其中涉及到的部分我们在后面会一一道来。现在我们先从查询优化部分开始。
sql是我们和数据库交流最重要的部分,所以我们在调优的时候,需要花费的大量时间就在sql调优上面。常见的分析手段有慢查询日志,EXPLAIN 分析查询,通过定位分析性能的瓶颈,才能更好的优化数据库系统的性能。
慢查询
配置文件my.cnf[linux]或my.ini[windows]中在[mysqld]一行下面加入两个配置参数
log-slow-queries=C:/ProgramData/MySQL/MySQL Server 5.5/Data/mysqldata/slow-query.log
long_query_time=5
log-slow-queries参数为慢查询日志存放的位置,一般这个目录要有mysql的运行帐号的可写权限,一般都将这个目录设置为mysql的数据存放目录; long_query_time=5中的5表示查询超过五秒才记录;
还可以在my.cnf或者my.ini中添加log-queries-not-using-indexes参数,表示记录下没有使用索引的查询。
SHOW VARIABLES LIKE 'long_query_time%';
show global status like '%Slow_queries%';
慢查询分析
我们可以通过打开log文件查看得知哪些SQL执行效率低下 ,从日志中,可以发现查询时间超过5 秒的SQL,而小于5秒的没有出现在此日志中。
如果慢查询日志中记录内容很多,可以使用mysqldumpslow工具(MySQL客户端安装自带)来对慢查询日志进行分类汇总。mysqldumpslow对日志文件进行了分类汇总,显示汇总后摘要结果。
进入log的存放目录,运行:
[root@mysql_data]
Reading mysql slow query log fromslow-query.log
Count: 2 Time=11.00s (22s) Lock=0.00s (0s)Rows=1.0 (2), root[root]@mysql
select count(N) from t_user;
mysqldumpslow命令
/path/mysqldumpslow -s c -t 10/database/mysql/slow-query.log
这会输出记录次数最多的10条SQL语句,其中:
-s, 是表示按照何种方式排序,c、t、l、r分别是按照记录次数、时间、查询时间、返回的记录数来排序,ac、at、al、ar,表示相应的倒叙
-t, 是top n的意思,即为返回前面多少条的数据;
-g, 后边可以写一个正则匹配模式,大小写不敏感的;
例如:
/path/mysqldumpslow -s r -t 10/database/mysql/slow-log
得到返回记录集最多的10个查询。
/path/mysqldumpslow -s t -t 10 -g “leftjoin” /database/mysql/slow-log
得到按照时间排序的前10条里面含有左连接的查询语句。
使用mysqldumpslow命令可以非常明确的得到各种我们需要的查询语句,对MySQL查询语句的监控、分析、优化是MySQL优化非常重要的一步。开启慢查询日志后,由于日志记录操作,在一定程度上会占用CPU资源影响mysql的性能,但是可以阶段性开启来定位性能瓶颈。
EXPLAIN执行计划(解析计划)
EXPLAIN可以帮助开发人员分析SQL问题,EXPLAIN显示了MySQL如何使用使用SQL执行计划,可以帮助开发人员写出更优化的查询语句。使用方法,在select语句前加上Explain就可以了:
EXPLAIN SELECT * FROM products
结果的列的说明如下:
1) id
SELECT识别符。这是SELECT查询序列号。这个不重要。它只是标识执行的sql语句中有多个查询而已
2) select_type
表示SELECT语句的类型。
- simple:简单select(不使用union或子查询)。
- primary:最外面的select。
- union:union中的第二个或后面的select语句。
- dependent union:union中的第二个或后面的select语句,取决于外面的查询。
- union result:union的结果。
- subquery:子查询中的第一个select。
- dependent subquery:子查询中的第一个select,取决于外面的查询。
- derived:导出表的select(from子句的子查询)。
- table
显示这查询的数据是关于哪张表的。
4) type 【重要】
区分索引,这是重要的列,显示连接使用了何种类型。从最好到最差的连接类型为:
system > const > eq_ref > ref > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL
一般来说,得保证查询至少达到range级别,最好能达到ref,到index就可以。
- system:表仅有一行,这是const类型的特列,平时不会出现,这个也可以忽略不计。
- const:数据表最多只有一个匹配行,因为只匹配一行数据,所以很快
- eq_ref:mysql手册是这样说的:“对于每个来自于前面的表的行组合,从该表中读取一行。这可能是最好的联接类型,除了const类型。它用在一个索引的所有部分被联接使用并且索引是UNIQUE或PRIMARY KEY”。eq_ref可以用于使用=做条件比较,且该列是索引的列。
- ref:查询条件索引既不是UNIQUE也不是PRIMARY KEY的情况。ref可用于=或<或>操作符的带索引的列。
- ref_or_null:该联接类型如同ref,但是添加了MySQL可以专门搜索包含NULL值的行。在解决子查询中经常使用该联接类型的优化。
- index_merge:该联接类型表示使用了索引合并优化方法。在这种情况下,key列包含了使用的索引的清单,key_len包含了使用的索引的最长的关键元素。
- unique_subquery:该类型替换了下面形式的IN子查询的ref: value IN (SELECT primary_key FROM single_table WHERE some_expr) unique_subquery是一个索引查找函数,可以完全替换子查询,效率更高。
- index_subquery:该联接类型类似于unique_subquery。可以替换IN子查询,但只适合下列形式的子查询中的非唯一索引: value IN (SELECT key_column FROM single_table WHERE some_expr)
- range:只检索给定范围的行,使用一个索引来选择行。
- index:该联接类型与ALL相同,除了只有索引树被扫描。这通常比ALL快,因为索引文件通常比数据文件小。
- ALL:对于每个来自于先前的表的行组合,进行完整的表扫描。(性能最差,去除all,最少要有index)
5) possible_keys
指出MySQL能使用哪个索引在该表中找到行。如果是空的,没有相关的索引。这时要提高性能,可通过检验WHERE子句,看是否引用某些字段,或者检索字段不是适合索引。
6) key
实际使用到的索引。如果为NULL,则没有使用索引。如果为primary的话,表示使用了主键。
7) key_len
最长的索引宽度。如果键是NULL,长度就是NULL。在不损失精确性的情况下,长度越短越好。
(能力有限,比较了解索引的结构,具备对索引调优的能力,算法比较强)
8) ref
显示使用哪个列或常数与key一起从表中选择行。
9) rows
显示MySQL认为它执行查询时必须检查的行数。行数越少效率越高(是否有索引)
10) Extra
执行状态说明,该列包含MySQL解决查询的详细信息,这个列的值有可能多个以下的情况
- Distinct: MySQL发现第1个匹配行后,停止为当前的行组合搜索更多的行。
- Not exists: MySQL能够对查询进行LEFT JOIN优化,发现1个匹配LEFT JOIN标准的行后,不再为前面的的行组合在该表内检查更多的行。
- range checked for each record (index map: #): MySQL没有发现好的可以使用的索引,但发现如果来自前面的表的列值已知,可能部分索引可以使用。
- 【Using filesort】: MySQL需要额外的一次传递,以找出如何按排序顺序检索行。排序上,出现这个意为着排序时需要读取真实数据记录【读取数据文件IO】
- Using index: 从只使用索引树中的信息而不需要进一步搜索读取实际的行来检索表中的列信息。
- 【Using temporary】:为了解决复杂查询,MySQL需要创建一个临时表来容纳结果,效率较低。
- Using where:WHERE 子句用于限制哪一个行匹配下一个表或发送到客户。
- Using sort_union(…), Using union(…), Using intersect(…):这些函数说明如何为index_merge联接类型合并索引扫描。
- Using index for group-by:类似于访问表的Using index方式,Using index for group-by表示MySQL发现了一个索引,可以用来查 询GROUP BY或DISTINCT查询的所有列,而不要额外搜索硬盘访问实际表中的数据。
写在后面:下一篇准备写关于索引优化
|