數據庫性能分析利器—執行計劃

   在講這個問題以前,說一種現象,你們可能會常常遇到這樣的狀況感受數據庫好慢啊,數據庫cpu咋這麼高,內存好像不夠用了,應該咋辦呢,不少同窗一臉茫然,也有少部分同窗能說建索引,讀寫分離,分庫分表這樣大而化之的優化策略,那具體到執行層面如何細化呢?相信絕大多數測試同窗已經進入盲區,數據庫的優化是從定性到定量的過程,好比是否沿着索引查詢,掃描減小了多少行,文件排序減小沒,tps提高多少等等,今天就從sql自己給你們剖析數據庫執行計劃。mysql

  數據庫執行計劃是啥意思?在我看來,就是告訴我這條sql須要作什麼,它是怎麼作的,經過執行計劃咱們能分析出sql的性能以及改進思路。咱們以主流的mysql爲例,給你們舉例如何作執行計劃;sql

在mysql中,執行計劃在sql語句前加上explain便可,如圖1,你們已經看到出現了不少行,數據庫

圖1服務器

我先逐行解釋下字面意思函數

 

Id 性能

表明select語句的編號,至關於標識做用;測試

Select_type優化

查詢類型,常見的例如:simple(不含子查詢),primary(含子查詢或派生查詢);spa

Tablecode

所查詢的表;

Partitions

通常查看錶分區狀況;

Type

查詢方式,數據庫性能診斷的重要依據,通常有all,index,ref,eq_ref,這一行是重點,你們先記住很重要!

Possible_keys和key:possible_keys列

指出MySQL能使用哪一個索引在該表中找到行。而下面的key是MYSQL實際用到的索引,這意味着在possible_keys中的是計劃中的,而key是實際的,也就是計劃中有這個索引,實際執行時未必能用到

Key_len: 顯示MySQL決定使用的鍵長度。若是KEY鍵是NULL,則長度爲NULL。
使用的索引的長度。在不損失精確性的狀況下,長度越短越好;

row

表示MySQL根據表統計信息及索引選用狀況,估算的找到所需的記錄所須要讀取的行數。

Extra

包含不適合在其餘列中顯示但十分重要的額外信息,請記住,這行也很重要;

Using index:該值表示相應的select操做中使用了覆蓋索引(Covering Index)

MySQL能夠利用索引返回select列表中的字段,而沒必要根據索引再次讀取數據文件  包含全部知足查詢須要的數據的索引稱爲 覆蓋索引

Using where:表示MySQL服務器在存儲引擎受到記錄後進行「後過濾」(Post-filter),若是查詢未能使用索引,Using where的做用只是提醒咱們MySQL將用where子句來過濾結果集

Using temporary:表示MySQL須要使用臨時表來存儲結果集,常見於排序和分組查詢

Using filesort:  MySQL中沒法利用索引完成的排序操做稱爲「文件排序」

 

 

 

下面咱們重點解析type列,會結合官網的案例進行說明,咱們按性能從高到低的順序展現;

1、system 
這是const的一個特例聯接類型。表只有一行(=系統表)。

mysql> explain select * from (select * from customer where customer_id=1) a;
+----+-------------+------------+--------+---------------+---------+---------+-------+------+-------+
| id | select_type | table      | type   | possible_keys | key     | key_len | ref   | rows | Extra |
+----+-------------+------------+--------+---------------+---------+---------+-------+------+-------+
|  1 | PRIMARY     | <derived2> | system | NULL          | NULL    | NULL    | NULL  |    1 | NULL  |
|  2 | DERIVED     | customer   | const  | PRIMARY       | PRIMARY | 2       | const |    1 | NULL  |
+----+-------------+------------+--------+---------------+---------+---------+-------+------+-------+
2 rows in set

2、const

表最多有一個匹配行,它將在查詢開始時被讀取。由於僅有一行,在這行的列值可被優化器剩餘部分認爲是常數。const表很快,由於它們只讀取一次! 
const用於用常數值比較PRIMARY KEY或UNIQUE索引的全部部分時。在下面的查詢中,tbl_name能夠用於const表:

SELECT * from tbl_name WHERE primary_key=1;
SELECT * from tbl_name WHERE primary_key_part1=1和 primary_key_part2=2;

3、eq_ref

對於每一個來自於前面的表的行組合,從該表中讀取一行。這多是最好的聯接類型,除了const類型。它用在一個索引的全部部分被聯接使用而且索引是UNIQUE或PRIMARY KEY。 
eq_ref能夠用於使用= 操做符比較的帶索引的列。比較值能夠爲常量或一個使用在該表前面所讀取的表的列的表達式。
在下面的例子中,MySQL可使用eq_ref聯接來處理ref_tables:

SELECT * FROM ref_table,other_table
  WHERE ref_table.key_column=other_table.column;
 
SELECT * FROM ref_table,other_table
  WHERE ref_table.key_column_part1=other_table.column
  AND ref_table.key_column_part2=1;
# 相對於下面的ref區別就是它使用的惟一索引,即主鍵或惟一索引,而ref使用的是非惟一索引或者普通索引。id是主鍵
mysql> explain select a.*,b.* from testa a,testb b where a.id=b.id
;
+----+-------------+-------+--------+---------------+---------+---------+-------------+------+-------------+
| id | select_type | table | type   | possible_keys | key     | key_len | ref         | rows | Extra       |
+----+-------------+-------+--------+---------------+---------+---------+-------------+------+-------------+
|  1 | SIMPLE      | b     | ALL    | NULL          | NULL    | NULL    | NULL        |    1 | Using where |
|  1 | SIMPLE      | a     | eq_ref | PRIMARY       | PRIMARY | 4       | sakila.b.id |    1 | NULL        |
+----+-------------+-------+--------+---------------+---------+---------+-------------+------+-------------+
2 rows in set

4、ref

對於每一個來自於前面的表的行組合,全部有匹配索引值的行將從這張表中讀取。若是聯接只使用鍵的最左邊的前綴,或若是鍵不是UNIQUE或PRIMARY KEY(換句話說,若是聯接不能基於關鍵字選擇單個行的話),則使用ref。若是使用的鍵僅僅匹配少許行,該聯接類型是不錯的。 
ref能夠用於使用=或<=>操做符的帶索引的列。 
在下面的例子中,MySQL可使用ref聯接來處理ref_tables:

SELECT * FROM ref_table WHERE key_column=expr;
 
SELECT * FROM ref_table,other_table
  WHERE ref_table.key_column=other_table.column;
 
SELECT * FROM ref_table,other_table
  WHERE ref_table.key_column_part1=other_table.column
  AND ref_table.key_column_part2=1;
# 使用非惟一性索引或者惟一索引的前綴掃描,返回匹配某個單獨值的記錄行。name有非惟一性索引
mysql> explain select * from testa where name='aaa';
+----+-------------+-------+------+---------------+----------+---------+-------+------+-----------------------+
| id | select_type | table | type | possible_keys | key      | key_len | ref   | rows | Extra                 |
+----+-------------+-------+------+---------------+----------+---------+-------+------+-----------------------+
|  1 | SIMPLE      | testa | ref  | idx_name      | idx_name | 33      | const |    2 | Using index condition |
+----+-------------+-------+------+---------------+----------+---------+-------+------+-----------------------+
1 row in set
 
mysql> explain select a.*,b.* from testa a,testb b where a.name=b.cname;
+----+-------------+-------+------+---------------+----------+---------+----------------+------+-------------+
| id | select_type | table | type | possible_keys | key      | key_len | ref            | rows | Extra       |
+----+-------------+-------+------+---------------+----------+---------+----------------+------+-------------+
|  1 | SIMPLE      | b     | ALL  | NULL          | NULL     | NULL    | NULL           |    1 | Using where |
|  1 | SIMPLE      | a     | ref  | idx_name      | idx_name | 33      | sakila.b.cname |    1 | NULL        |
+----+-------------+-------+------+---------------+----------+---------+----------------+------+-------------+
2 rows in set

5、 fulltext

使用FULLTEXT索引進行聯接。

6、ref_or_null

該聯接類型如同ref,可是添加了MySQL能夠專門搜索包含NULL值的行。在解決子查詢中常用該聯接類型的優化。
在下面的例子中,MySQL可使用ref_or_null聯接來處理ref_tables:

SELECT * FROM ref_table
  WHERE key_column=expr OR key_column IS NULL;
mysql> explain select * from (select cusno from testa t1,testb t2 where t1.id=t2.id) t where cusno =2 or cusno is null;
+----+-------------+------------+-------------+---------------+-------------+---------+--------------+------+--------------------------+
| id | select_type | table      | type        | possible_keys | key         | key_len | ref          | rows | Extra                    |
+----+-------------+------------+-------------+---------------+-------------+---------+--------------+------+--------------------------+
|  1 | PRIMARY     | <derived2> | ref_or_null | <auto_key0>   | <auto_key0> | 5       | const        |    2 | Using where; Using index |
|  2 | DERIVED     | t2         | index       | PRIMARY       | PRIMARY     | 4       | NULL         |    1 | Using index              |
|  2 | DERIVED     | t1         | eq_ref      | PRIMARY       | PRIMARY     | 4       | sakila.t2.id |    1 | NULL                     |
+----+-------------+------------+-------------+---------------+-------------+---------+--------------+------+--------------------------+
3 rows in set

此處按照官網的格式未測試出例子來,如有例子的請留言,我測試更新

7、index_merge

該聯接類型表示使用了索引合併優化方法。在這種狀況下,key列包含了使用的索引的清單,key_len包含了使用的索引的最長的關鍵元素。

此處按照官網的格式未測試出例子來,如有例子的請留言,我測試更新

8、unique_subquery

unique_subquery是一個索引查找函數,能夠徹底替換子查詢,效率更高。
該類型替換了下面形式的IN子查詢的ref:

value IN (SELECT primary_key FROM single_table WHERE some_expr)

此處按照官網的格式未測試出例子來,如有例子的請留言,我測試更新

9、index_subquery

該聯接類型相似於unique_subquery。能夠替換IN子查詢,但只適合下列形式的子查詢中的非惟一索引:

value IN (SELECT key_column FROM single_table WHERE some_expr)

此處按照官網的格式未測試出例子來,如有例子的請留言,我測試更新

10、range

只檢索給定範圍的行,使用一個索引來選擇行。key列顯示使用了哪一個索引。key_len包含所使用索引的最長關鍵元素。在該類型中ref列爲NULL。 
當使用=、<>、>、>=、<、<=、IS NULL、<=>、BETWEEN或者IN操做符,用常量比較關鍵字列時,可使用range

SELECT * FROM tbl_name
  WHERE key_column = 10;
 
SELECT * FROM tbl_name
  WHERE key_column BETWEEN 10 and 20;
 
SELECT * FROM tbl_name
  WHERE key_column IN (10,20,30);
 
SELECT * FROM tbl_name
  WHERE key_part1 = 10 AND key_part2 IN (10,20,30);

11、index

索引類型與ALL類型同樣,除了它是走索引樹掃描的,它有兩種方式:

若是該覆蓋索引能知足查詢的全部數據,那僅僅掃描這索引樹。在這種狀況下,Extra列就會顯示用Using index。通常僅僅用索引是掃描的比ALL掃描的要快,由於索引樹比表數據小不少。

全表掃描被用到從索引中去讀取數據, Extra列就不會顯示用Using index

若是查詢僅僅是索引列,那MySQL會這個index索引類型

mysql> alter table testa add primary key p_id(id);
Query OK, 0 rows affected
Records: 0  Duplicates: 0  Warnings: 0
 
mysql> create index idx_name on testa(name);
Query OK, 0 rows affected
Records: 0  Duplicates: 0  Warnings: 0
 
mysql> insert into testa values(2,2,'aaa');
Query OK, 1 row affected
 
# 由於查詢的列name上建有索引,因此若是這樣type走的是index
mysql> explain select name from testa;
+----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+
| id | select_type | table | type  | possible_keys | key      | key_len | ref  | rows | Extra       |
+----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+
|  1 | SIMPLE      | testa | index | NULL          | idx_name | 33      | NULL |    2 | Using index |
+----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+
1 row in set
 
# 由於查詢的列cusno沒有建索引,或者查詢的列包含沒有索引的列,這樣查詢就會走ALL掃描,以下:
mysql> explain select cusno from testa;
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
|  1 | SIMPLE      | testa | ALL  | NULL          | NULL | NULL    | NULL |    2 | NULL  |
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
1 row in set
 
# *包含有未見索引的列
mysql> explain select * from testa;
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
|  1 | SIMPLE      | testa | ALL  | NULL          | NULL | NULL    | NULL |    2 | NULL  |
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
1 row in set

12、all

對於每一個來自於先前的表的行組合,進行完整的表掃描。若是表是第一個沒標記const的表,這一般很差,而且一般在它狀況下不好。一般能夠增長更多的索引而不要使用ALL,使得行能基於前面的表中的常數值或列值被檢索出。

 

好了,以上就是此次文章想說的,但願對你們有幫助,有錯誤,也但願你們指出。

相關文章
相關標籤/搜索