優化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