1. 程式人生 > 資料庫 >MySQL中Explain初識

MySQL中Explain初識

Index

MySQL索引的基本操作

CREATE INDEX idx_price on OrderItems(item_price); 
ALTER TABLE OrderItems DROP INDEX idx_order_num_price;

 

Explain

MySQL 提供了一個 EXPLAIN 命令, 它可以對 SELECT 語句進行分析, 並輸出 SELECT 執行的詳細資訊, 以供開發人員針對性優化。EXPLAIN 命令用法十分簡單, 在 SELECT 語句前加上 Explain 就可以了。

Explain各列的含義如下:

id: 每個 SELECT 都會自動分配一個唯一的識別符號
select_type: SELECT 查詢的型別
table: 查詢的是哪個表
partitions: 匹配的分割槽
type: 訪問型別
possible_keys: 此次查詢中可能選用的索引
key: 此次查詢中確切使用到的索引
ref: 哪個欄位或常數與 key 一起被使用
rows: 顯示此查詢一共掃描了多少行 這個是一個估計值
filtered: 表示此查詢條件所過濾的資料的百分比
extra: 額外的資訊

其中Type是重點關注的欄位,用以快速評價查詢語句效能,常見type效能從差到好如下:

All < Index < Range < Ref < const

 

Demo

以OrderItems表為例:

| OrderItems | CREATE TABLE `OrderItems` (
  `order_num` int(11) NOT NULL,
  `order_item` int(11) NOT NULL,
  `prod_id` char(10) NOT NULL,
  `quantity` int(11) NOT NULL,
  `item_price` decimal(8,2) NOT NULL,
  PRIMARY KEY (`order_num`,`order_item`),
  KEY `FK_OrderItems_Products` (`prod_id`),
  KEY `idx_price` (`item_price`),
  KEY `idx_order_num_price` (`order_num`,`item_price`),
  constRAINT `FK_OrderItems_Orders` FOREIGN KEY (`order_num`) REFERENCES `Orders` (`order_num`),
  CONSTRAINT `FK_OrderItems_Products` FOREIGN KEY (`prod_id`) REFERENCES `Products` (`prod_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |


mysql> select * from OrderItems;
+-----------+------------+---------+----------+------------+
| order_num | order_item | prod_id | quantity | item_price |
+-----------+------------+---------+----------+------------+
|     20005 |          1 | BR01    |      100 |       5.49 |
|     20005 |          2 | BR03    |      100 |      10.99 |
|     20006 |          1 | BR01    |       20 |       5.99 |
|     20006 |          2 | BR02    |       10 |       8.99 |
|     20006 |          3 | BR03    |       10 |      11.99 |
|     20007 |          1 | BR03    |       50 |      11.49 |
|     20007 |          2 | BNBG01  |      100 |       2.99 |
|     20007 |          3 | BNBG02  |      100 |       2.99 |
|     20007 |          4 | BNBG03  |      100 |       2.99 |
|     20007 |          5 | RGAN01  |       50 |       4.49 |
|     20008 |          1 | RGAN01  |        5 |       4.99 |
|     20008 |          2 | BR03    |        5 |      11.99 |
|     20008 |          3 | BNBG01  |       10 |       3.49 |
|     20008 |          4 | BNBG02  |       10 |       3.49 |
|     20008 |          5 | BNBG03  |       10 |       3.49 |
|     20009 |          1 | BNBG01  |      250 |       2.49 |
|     20009 |          2 | BNBG02  |      250 |       2.49 |
|     20009 |          3 | BNBG03  |      250 |       2.49 |
+-----------+------------+---------+----------+------------+
18 rows in set (0.03 sec)

舉例說明幾個Explain type的查詢:

all,直接全表查詢

mysql> explain select item_price from OrderItems;
+----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+-------+
| id | select_type | table      | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra |
+----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+-------+
|  1 | SIMPLE      | OrderItems | NULL       | ALL  | NULL          | NULL | NULL    | NULL |   18 |   100.00 | NULL  |
+----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+-------+

index,利用索引

mysql> explain select order_num from OrderItems;    
+----+-------------+------------+------------+-------+---------------+------------------------+---------+------+------+----------+-------------+
| id | select_type | table      | partitions | type  | possible_keys | key                    | key_len | ref  | rows | filtered | Extra       |
+----+-------------+------------+------------+-------+---------------+------------------------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | OrderItems | NULL       | index | NULL          | FK_OrderItems_Products | 30      | NULL |   18 |   100.00 | Using index |
+----+-------------+------------+------------+-------+---------------+------------------------+---------+------+------+----------+-------------+

range,對索引進行範圍查詢,多見於between/in/<>等關鍵字

mysql> explain select order_num from OrderItems where order_num between 20003 and 20005;
+----+-------------+------------+------------+-------+---------------+---------+---------+------+------+----------+--------------------------+
| id | select_type | table      | partitions | type  | possible_keys | key     | key_len | ref  | rows | filtered | Extra                    |
+----+-------------+------------+------------+-------+---------------+---------+---------+------+------+----------+--------------------------+
|  1 | SIMPLE      | OrderItems | NULL       | range | PRIMARY       | PRIMARY | 4       | NULL |    2 |   100.00 | Using where; Using index |
+----+-------------+------------+------------+-------+---------------+---------+---------+------+------+----------+--------------------------+

ref,使用普通索引

mysql> explain select order_num from OrderItems where order_num = 20005;
+----+-------------+------------+------------+------+---------------+---------+---------+-------+------+----------+-------------+
| id | select_type | table      | partitions | type | possible_keys | key     | key_len | ref   | rows | filtered | Extra       |
+----+-------------+------------+------------+------+---------------+---------+---------+-------+------+----------+-------------+
|  1 | SIMPLE      | OrderItems | NULL       | ref  | PRIMARY       | PRIMARY | 4       | const |    2 |   100.00 | Using index |
+----+-------------+------------+------------+------+---------------+---------+---------+-------+------+----------+-------------+

consts 單表中最多隻有一個匹配行(主鍵或者唯一索引),在優化階段即可讀取到數。

 

Optimization

通過分析Explain語句結果可以優化查詢效能。一般關注點有:一是type往好的方向優化,二是有order by時,儘量不要在extra中出現Using filesort。

type優化,如 阿里巴巴Java程式設計規範 中定義的那樣,一般不允許all和index的查詢,會極大影響效能。儘量優化至range以上。方法一般就是建索引,不要為了節省插入效能而去縮減必要的索引。

mysql> CREATE INDEX idx_price on OrderItems(item_price);

然後再執行關於item_price的查詢:

mysql> explain select order_num from OrderItems where item_price between 5 and 8;
+----+-------------+------------+------------+-------+---------------+-----------+---------+------+------+----------+--------------------------+
| id | select_type | table      | partitions | type  | possible_keys | key       | key_len | ref  | rows | filtered | Extra                    |
+----+-------------+------------+------------+-------+---------------+-----------+---------+------+------+----------+--------------------------+
|  1 | SIMPLE      | OrderItems | NULL       | range | idx_price     | idx_price | 4       | NULL |    2 |   100.00 | Using where; Using index |
+----+-------------+------------+------------+-------+---------------+-----------+---------+------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)

看到type位置變為range,possible_keys和key的位置出現了我們新建的索引

去除Using filesort

執行下列查詢

mysql> explain select * from OrderItems where order_num = 20003 order by item_price;         
+----+-------------+------------+------------+------+---------------+---------+---------+-------+------+----------+-----------------------------+
| id | select_type | table      | partitions | type | possible_keys | key     | key_len | ref   | rows | filtered | Extra                       |
+----+-------------+------------+------------+------+---------------+---------+---------+-------+------+----------+-----------------------------+
|  1 | SIMPLE      | OrderItems | NULL       | ref  | PRIMARY       | PRIMARY | 4       | const |    1 |   100.00 | Using where; Using filesort |
+----+-------------+------------+------------+------+---------------+---------+---------+-------+------+----------+-----------------------------+
1 row in set, 1 warning (0.00 sec)

使用了fileSort。我們建立索引並再次執行查詢如下:

mysql> CREATE INDEX idx_order_num_price on OrderItems(order_num, item_price);

mysql> explain select * from OrderItems where order_num = 20003 order by item_price;                
+----+-------------+------------+------------+------+-----------------------------+---------------------+---------+-------+------+----------+-----------------------+
| id | select_type | table      | partitions | type | possible_keys               | key                 | key_len | ref   | rows | filtered | Extra                 |
+----+-------------+------------+------------+------+-----------------------------+---------------------+---------+-------+------+----------+-----------------------+
|  1 | SIMPLE      | OrderItems | NULL       | ref  | PRIMARY,idx_order_num_price | idx_order_num_price | 4       | const |    1 |   100.00 | Using index condition |
+----+-------------+------------+------------+------+-----------------------------+---------------------+---------+-------+------+----------+-----------------------+
1 row in set, 1 warning (0.01 sec)

如果建立了多重索引A-B(A和B是column name),那麼查詢語句的where clause中僅使用了A也是可以利用該A-B索引的。事實上,只要查詢條件從左至右依次匹配某索引,都是可以利用的。

https://www.renrenfan.com.cn https://www.houdianzi.com

Problems

另外遇到兩個關於索引有序性使用的小坑。

查詢語句中的欄位匹配索引的前半部分,但如果它們是用於in/between,索引失效

比如在建立了order_num/item_price索引的情況下,還是會fileSort:

mysql> explain select order_num from OrderItems where order_num between 20003 and 20005 order by item_price;
+----+-------------+------------+------------+-------+---------------+---------+---------+------+------+----------+-----------------------------+
| id | select_type | table      | partitions | type  | possible_keys | key     | key_len | ref  | rows | filtered | Extra                       |
+----+-------------+------------+------------+-------+---------------+---------+---------+------+------+----------+-----------------------------+
|  1 | SIMPLE      | OrderItems | NULL       | range | PRIMARY       | PRIMARY | 4       | NULL |    2 |   100.00 | Using where; Using filesort |
+----+-------------+------------+------------+-------+---------------+---------+---------+------+------+----------+-----------------------------+

單一Order時使用索引不分升降序,但如果對多欄位排序,則要求索引順序和查詢語句’一致’

mysql> explain select item_price from OrderItems ORDER BY order_num, item_price;  
+----+-------------+------------+------------+-------+---------------+---------------------+---------+------+------+----------+-------------+
| id | select_type | table      | partitions | type  | possible_keys | key                 | key_len | ref  | rows | filtered | Extra       |
+----+-------------+------------+------------+-------+---------------+---------------------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | OrderItems | NULL       | index | NULL          | idx_order_num_price | 8       | NULL |   18 |   100.00 | Using index |
+----+-------------+------------+------------+-------+---------------+---------------------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

mysql> explain select item_price from OrderItems ORDER BY order_num, item_price desc;
+----+-------------+------------+------------+-------+---------------+-----------+---------+------+------+----------+-----------------------------+
| id | select_type | table      | partitions | type  | possible_keys | key       | key_len | ref  | rows | filtered | Extra                       |
+----+-------------+------------+------------+-------+---------------+-----------+---------+------+------+----------+-----------------------------+
|  1 | SIMPLE      | OrderItems | NULL       | index | NULL          | idx_price | 4       | NULL |   18 |   100.00 | Using index; Using filesort |
+----+-------------+------------+------------+-------+---------------+-----------+---------+------+------+----------+-----------------------------+
1 row in set, 1 warning (0.00 sec)

mysql> explain select item_price from OrderItems ORDER BY order_num desc, item_price desc;
+----+-------------+------------+------------+-------+---------------+---------------------+---------+------+------+----------+-------------+
| id | select_type | table      | partitions | type  | possible_keys | key                 | key_len | ref  | rows | filtered | Extra       |
+----+-------------+------------+------------+-------+---------------+---------------------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | OrderItems | NULL       | index | NULL          | idx_order_num_price | 8       | NULL |   18 |   100.00 | Using index |
+----+-------------+------------+------------+-------+---------------+---------------------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

如果理解了MySQL索引的物理實現(B+ Tree),這些應該就比較容易理解了(TODO)。