优化SQL语句的通常步骤:
1. 经过 show status 命令了解各类SQL的执行效率。
html
show status like 'com_%'
Com_xxx表示每一个xxx语句执行的次数,咱们一般比较关心的是一下几个参数:
Com_select: 执行SELECT 操做的次数,一次查询累加1
Com_insert: 执行INSERT 操做的次数,对于批量插入的INSERT操做,只累加一次。
Com_updata: 执行UPDATA 操做的次数。
Com_delete: 执行DELETE 操做的次数。
能够很容易地了解当前数据库的应用是以插入更新为主仍是以查询操做为主,以及各类
数据类型的SQL大体的执行比例是多少。对于更新操做的计数,是对执行次数的计数,
不是提交仍是回滚都会进行累加。
对于事务型的应用,经过Com_commit和Com_rollback 能够了解事务提交和回滚的状况,
对于回滚操做很是频繁的数据库,可意味着应用编写存在问题。
此外,如下几个参数便于用户了解数据库的基本状况。
connections: 试图链接mysql服务器的次数。
Uptime: 服务器工做时间。
Slow_queries: 慢查询的次数。
mysql
2. 定位执行效率较低的SQL语句
1)经过慢查询日志定位那些执行效率较低的SQL语句.
慢查询日志记录了全部执行时间超过参数long_query_time(单位:秒)设置值而且扫描记录数不小于
min_examined_row_limit的全部SQL语句的日志(注意:获取表锁定的时间不算做执行时间)。
long_query_time默认为10秒,最小为0,精确能够到毫秒。sql
查看慢日志存放的位置: 数据库
mysql> show variables like 'slow_query_log_file'; +---------------------+-----------------------------------+ | Variable_name | Value | +---------------------+-----------------------------------+ | slow_query_log_file | /var/lib/mysql/localhost-slow.log | +---------------------+-----------------------------------+ 1 row in set (0.00 sec)
开启慢查询:vim
mysql> set global slow_query_log = "ON"; Query OK, 0 rows affected (0.04 sec)
查看慢查询是否开启:服务器
mysql> show variables like 'slow_query%'; +---------------------+-----------------------------------+ | Variable_name | Value | +---------------------+-----------------------------------+ | slow_query_log | ON | | slow_query_log_file | /var/lib/mysql/localhost-slow.log | +---------------------+-----------------------------------+ 2 rows in set (0.00 sec)
查询一下long_query_time的值socket
mysql> show variables like "long%"; +-----------------+-----------+ | Variable_name | Value | +-----------------+-----------+ | long_query_time | 10.000000 | +-----------------+-----------+ 1 row in set (0.00 sec)
为了方便测试,将修改慢查询时间为0.1秒函数
mysql> set long_query_time=0.1; Query OK, 0 rows affected (0.00 sec) mysql> show variables like "long%"; +-----------------+----------+ | Variable_name | Value | +-----------------+----------+ | long_query_time | 0.100000 | +-----------------+----------+ 1 row in set (0.00 sec)
执行下面的语句:性能
mysql> select sleep(2); +----------+ | sleep(2) | +----------+ | 0 | +----------+ 1 row in set (2.00 sec)
查看日志文件:测试
[root@localhost mysql]# vim localhost-slow.log /usr/sbin/mysqld, Version: 5.6.20 (MySQL Community Server (GPL)). started with: Tcp port: 3306 Unix socket: /var/lib/mysql/mysql.sock Time Id Command Argument # Time: 151216 19:00:33 # User@Host: root[root] @ localhost [] Id: 5 # Query_time: 2.000178 Lock_time: 0.000000 Rows_sent: 1 Rows_examined: 0 use nms_db; SET timestamp=1450263633; select sleep(2);
2)慢查询日志在查询结束之后才记录,全部在应用反映执行效率出现问题的时候查询慢查询日志并
不能定位问题,可使用show processlist 命令查看当前MySQL 在进行的线程,包括线程的状态,
是否锁表等,能够实时的查看SQL的执行状况,同时对一些锁表操做进行优化。
3.经过Explain分析执行低效SQL的执行计划
经过以上步骤查询到低效的SQL语句后,能够经过EXPLAIN命令获取MYSQL执行的SELECT 语句的信息,
包括在SELECT 语句执行过程当中表如何链接和链接的顺序。
explain select count(*) from warning_repaired\G; *************************** 1. row *************************** id: 1 select_type: SIMPLE table: warning_repaired type: index possible_keys: NULL key: id key_len: 4 ref: NULL rows: 483078 Extra: Using index 1 row in set (0.00 sec)
select_type: 表示SELECT的类型,常见的取值有SIMPLE(简单表,即不使用表链接或者子查询)
PRLMARY(主查询,即外层的查询), UNION(UNION 中的第二个或者后面的查询语句)
SUBQUERY(子查询中的第一个SELECT等)
table: 输入结果集的表
type: 表示MYSQL 在表中找到所需行的方式,或者访问类型,常见的类型以下:
ALL, index, range, ref, eq_ref, const,system, NULL
从左到右,性能由最差到最好。
(1) type = ALL,全表扫描,MySQL遍历全表来找到匹配的行
(2) type = index, 索引全扫描,MySQL遍历整个索引来查询匹配的行
(3) type = range, 索引范围扫描,常见于<, <=, >, >=, between等操做符
(4) type = ref, 使用非惟一索引扫描或惟一索引的前缀扫描,返回匹配某个单独值的记录行
(5) type = eq_ref,
(6) type = const/system,单表中最多有一个匹配行,查询起来很是迅速,因此这个匹配行中的其余
列的值能够被优化器在当前查询中看成常量来处理。
(7) type = NULL, MySQL不用访问表或者索引,直接就可以获得结果。
possible_keys: 表示查询时可能使用的索引
key: 表示实际使用的索引
key_len: 使用到索引字段的长度
rows: 扫描行的数量
Extra: 执行状况的说明和描述,包含不适合在其余列中显示可是对执行计划很是重要的额外信息
4. 经过show profile分析SQL
查看当前MySQL是否支持profile:
mysql> select @@have_profiling; +------------------+ | @@have_profiling | +------------------+ | YES | +------------------+ 1 row in set, 1 warning (0.02 sec)
默认profiling是关闭的,能够经过set语句在Session级别开启profiling:
mysql> select @@profiling; +-------------+ | @@profiling | +-------------+ | 0 | +-------------+ 1 row in set, 1 warning (0.00 sec) mysql> set profiling=1; Query OK, 0 rows affected, 1 warning (0.00 sec) mysql> select @@profiling; +-------------+ | @@profiling | +-------------+ | 1 | +-------------+ 1 row in set, 1 warning (0.00 sec)
查看执行时间:
mysql> show profiles; +----------+------------+---------------------------------------+ | Query_ID | Duration | Query | +----------+------------+---------------------------------------+ | 1 | 0.00017075 | select @@profiling | | 2 | 0.00023175 | show create table warning_repaired | | 3 | 0.12900950 | select count(*) from warning_repaired | +----------+------------+---------------------------------------+ 3 rows in set, 1 warning (0.00 sec)
经过show profile query 语句可以看到执行过程当中线程的每一个状态和消耗的时间:
mysql> show profile for query 3; +----------------------+----------+ | Status | Duration | +----------------------+----------+ | starting | 0.000065 | | checking permissions | 0.000009 | | Opening tables | 0.000026 | | init | 0.000022 | | System lock | 0.000011 | | optimizing | 0.000010 | | statistics | 0.000028 | | preparing | 0.000019 | | executing | 0.000004 | | Sending data | 0.128745 | | end | 0.000013 | | query end | 0.000009 | | closing tables | 0.000015 | | freeing items | 0.000022 | | cleaning up | 0.000014 | +----------------------+----------+ 15 rows in set, 1 warning (0.00 sec)
注意: Sending data 状态表示MySQL线程开启访问数据行并把结果返回给客户端,而不只仅是返回
结果给客户端,因为在Sending data 状态下,MySQL线程每每须要作大量的磁盘读取操做,因此常常
是整个查询中耗时最长的状态。
对比MyISAM 表的count(*) 操做,建立一个一样的表结构的MyISAM表,数据量也彻底一致:
mysql> show profiles; +----------+------------+-------------------------------------------------------------------+ | Query_ID | Duration | Query | +----------+------------+-------------------------------------------------------------------+ | 1 | 0.00017075 | select @@profiling | | 2 | 0.00023175 | show create table warning_repaired | | 3 | 0.12900950 | select count(*) from warning_repaired | | 4 | 0.66972975 | create table waring_repaired_myisam like warning_repaired | | 5 | 0.00040075 | show tables | | 6 | 0.00061900 | alter table warning_repaired_myisam engine=myisam | | 7 | 0.26157325 | alter table waring_repaired_myisam engine=myisam | | 8 | 0.00024950 | select count(*) from waring_reparied_myisam | | 9 | 0.01772475 | show create table waring_repaired_myisam | | 10 | 0.00021850 | select count(*) from waring_repaired_myisam | | 11 | 0.00028175 | insert into waring_repaired_myisam select * from payment | | 12 | 4.61870225 | insert into waring_repaired_myisam select * from warning_repaired | | 13 | 0.12892775 | select count(*) from warning_repaired | | 14 | 0.00021225 | select count(*) from waring_repaired_myisam | +----------+------------+-------------------------------------------------------------------+ mysql> show profile for query 14; +----------------------+----------+ | Status | Duration | +----------------------+----------+ | starting | 0.000067 | | checking permissions | 0.000009 | | Opening tables | 0.000026 | | init | 0.000019 | | System lock | 0.000013 | | optimizing | 0.000011 | | executing | 0.000012 | | end | 0.000006 | | query end | 0.000004 | | closing tables | 0.000013 | | freeing items | 0.000017 | | cleaning up | 0.000016 | +----------------------+----------+ 12 rows in set, 1 warning (0.00 sec) mysql> show profile for query 13; +----------------------+----------+ | Status | Duration | +----------------------+----------+ | starting | 0.000066 | | checking permissions | 0.000010 | | Opening tables | 0.000027 | | init | 0.000020 | | System lock | 0.000012 | | optimizing | 0.000008 | | statistics | 0.000020 | | preparing | 0.000018 | | executing | 0.000004 | | Sending data | 0.128666 | | end | 0.000018 | | query end | 0.000010 | | closing tables | 0.000016 | | freeing items | 0.000021 | | cleaning up | 0.000014 | +----------------------+----------+
从profile的结果可以看出,InnoDB引擎的表在COUNT(*)时经历了Sending data 状态,
存在访问数据的过程,而MyISAM引擎的表在executing以后直接就结束查询,彻底不须要访问数据。
关于MyISAM的神话
一个容易产生的误解是:MyISAM的COUNT()函数老是很是快,不过这是有前提条的,
即只有没有任何where条件的COUNT(*) 才很是快,由于此时无需实际地去计算表的行数。MySQL能够利用存储引擎的特性直接获取这个值。若是 MySQL 某列col不可能为NULL值,那么MySQL内部会将COUNT(col) 表达式优化成COUNT(*)。
5.经过 trace 分析优化器如何选择执行计划
暂略
参考文章:
http://www.cnblogs.com/hongfei/archive/2012/10/20/2732516.html