explain 命令詳解



explain 命令

在 select 語句之前增加 explain 關鍵字,MySQL 會在查詢上設置一個標記,從而在執行查詢時,會返回執行計划的信息,而不是執行這條 SQL 。

explain 命令可以獲取 MySQL 如何執行 SELECT 語句的信息,來查看一個這些 SQL 語句的執行計划,如該 SQL 語句有沒有使用上了索引、有沒有做全表掃描等。這是查詢性能優化不可缺少的一部分,因此平時在進行 SQL 開發時,都要養成用 explain 分析的習慣。

mysql> explain select * from actor;
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
|  1 | SIMPLE      | actor | ALL  | NULL          | NULL | NULL    | NULL |    2 | NULL  |
+----+-------------+-------+------+---------------+------+---------+------+------+-------+

expain 出來的信息有 10 列,分別是 id、select_type、table、type、possible_keys、key、key_len、ref、rows、Extra 。下面對這些字段出現的可能進行解釋:

列名 說明
id 執行編號,有幾個 select 就有幾個 id
select_type 表示本行是簡單的還是復雜的 select
table 正在訪問哪一個表(表名或別名)
type 表示關聯類型或訪問類型,即 MySQL 決定如何查找表中的行
possible_keys 哪些索引可以優化查詢
key 實際采用哪個索引來優化查詢
key_len 索引字段的長度
ref 顯示了之前的表在 key 列記錄的索引中查找值所用的列或常量
rows 為了找到所需的行而需要讀取的行數(估算值,並不精確)
Extra 執行情況的額外描述和說明
partitions
(MySQL 8 新增)
如果查詢是基於分區表的話,會顯示查詢將訪問的分區
filtered
(MySQL 8 新增)
按表條件過濾的行百分比。
rows * filtered/100 可以估算出將要和 explain 中前一個表進行連接的行數(前一個表指 explain 中的 id 值比當前表 id 值小的表)

explain 之后還可以通過 show warnings 命令得到優化后的查詢語句,從而看出優化器優化了什么。

mysql> explain extended select * from film where id = 1;
+----+-------------+-------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table | type  | possible_keys | key     | key_len | ref   | rows | filtered | Extra |
+----+-------------+-------+-------+---------------+---------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | film  | const | PRIMARY       | PRIMARY | 4       | const |    1 |   100.00 | NULL  |
+----+-------------+-------+-------+---------------+---------+---------+-------+------+----------+-------+

mysql> show warnings;
+-------+------+--------------------------------------------------------------------------------+
| Level | Code | Message                                                                        |
+-------+------+--------------------------------------------------------------------------------+
| Note  | 1003 | /* select#1 */ select '1' AS `id`,'film1' AS `name` from `test`.`film` where 1 |
+-------+------+--------------------------------------------------------------------------------+

id

id 列的編號是 select 的序列號,有幾個 select 就有幾個 id,且 id 越大的語句越先執行。

  • 如果是子查詢,則會有遞增的多個 id 值,那么 id 值越大表示優先級越高,越先被執行
  • id 值可能為 NULL,表示這一行是其他行的聯合結果;
  • id 如果相同,可以認為是一組,從上往下順序執行。

MySQL 將 select 查詢分為簡單查詢和復雜查詢。復雜查詢又分為三類:簡單子查詢、派生表(from 語句中的子查詢)、union 查詢。

簡單子查詢

mysql> explain select (select 1 from actor limit 1) from film;
+----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+
| id | select_type | table | type  | possible_keys | key      | key_len | ref  | rows | Extra       |
+----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+
|  1 | PRIMARY     | film  | index | NULL          | idx_name | 32      | NULL |    1 | Using index |
|  2 | SUBQUERY    | actor | index | NULL          | PRIMARY  | 4       | NULL |    2 | Using index |
+----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+ 

from 子句中的子查詢

mysql> explain select id from (select id from film) as der;
+----+-------------+------------+-------+---------------+----------+---------+------+------+-------------+
| id | select_type | table      | type  | possible_keys | key      | key_len | ref  | rows | Extra       |
+----+-------------+------------+-------+---------------+----------+---------+------+------+-------------+
|  1 | PRIMARY     | <derived2> | ALL   | NULL          | NULL     | NULL    | NULL |    2 | NULL        |
|  2 | DERIVED     | film       | index | NULL          | idx_name | 32      | NULL |    1 | Using index |
+----+-------------+------------+-------+---------------+----------+---------+------+------+-------------+

如上述查詢執行時有個臨時表別名為 der,外部 select 查詢引用了這個臨時表。

union 查詢

mysql> explain select 1 union all select 1;
+----+--------------+------------+------+---------------+------+---------+------+------+-----------------+
| id | select_type  | table      | type | possible_keys | key  | key_len | ref  | rows | Extra           |
+----+--------------+------------+------+---------------+------+---------+------+------+-----------------+
|  1 | PRIMARY      | NULL       | NULL | NULL          | NULL | NULL    | NULL | NULL | No tables used  |
|  2 | UNION        | NULL       | NULL | NULL          | NULL | NULL    | NULL | NULL | No tables used  |
| NULL | UNION RESULT | <union1,2> | ALL  | NULL          | NULL | NULL    | NULL | NULL | Using temporary |
+----+--------------+------------+------+---------------+------+---------+------+------+-----------------+

union 結果總是放在一個匿名臨時表中,因為臨時表不在 SQL 中出現,因此它的 id 是 NULL 。


select_type

select_type 表示對應行是簡單還是復雜的查詢,如果是復雜的查詢,又是上述三種復雜查詢中的哪一種。

  • simple:簡單查詢,即查詢不包含子查詢和 union。
mysql> explain select * from film where id = 2;
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
| id | select_type | table | type  | possible_keys | key     | key_len | ref   | rows | Extra |
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
|  1 | SIMPLE      | film  | const | PRIMARY       | PRIMARY | 4       | const |    1 | NULL  |
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
  • primary:復雜查詢中最外層的 select。
  • subquery:包含在 select 中的子查詢(不在 from 子句中)。
  • derived:包含在 from 子句中的子查詢。MySQL 會將結果存放在一個臨時表中,也稱為派生表(derived 的英文含義)。
mysql> explain select (select 1 from actor where id = 1) from (select * from film where id = 1) der;
+----+-------------+------------+--------+---------------+---------+---------+-------+------+-------------+
| id | select_type | table      | type   | possible_keys | key     | key_len | ref   | rows | Extra       |
+----+-------------+------------+--------+---------------+---------+---------+-------+------+-------------+
|  1 | PRIMARY     | <derived3> | system | NULL          | NULL    | NULL    | NULL  |    1 | NULL        |
|  3 | DERIVED     | film       | const  | PRIMARY       | PRIMARY | 4       | const |    1 | NULL        |
|  2 | SUBQUERY    | actor      | const  | PRIMARY       | PRIMARY | 4       | const |    1 | Using index |
+----+-------------+------------+--------+---------------+---------+---------+-------+------+-------------+ 
  • union:在 union 中的第二個和之后的 select 。
  • union result:從 union 臨時表檢索結果的 select 。
mysql> explain select 1 union all select 1;
+----+--------------+------------+------+---------------+------+---------+------+------+-----------------+
| id | select_type  | table      | type | possible_keys | key  | key_len | ref  | rows | Extra           |
+----+--------------+------------+------+---------------+------+---------+------+------+-----------------+
|  1 | PRIMARY      | NULL       | NULL | NULL          | NULL | NULL    | NULL | NULL | No tables used  |
|  2 | UNION        | NULL       | NULL | NULL          | NULL | NULL    | NULL | NULL | No tables used  |
| NULL | UNION RESULT | <union1,2> | ALL  | NULL          | NULL | NULL    | NULL | NULL | Using temporary |
+----+--------------+------------+------+---------------+------+---------+------+------+-----------------+
  • dependent union:首先需要滿足 UNION 的條件及 UNION 中第二個以及后面的 SELECT 語句,同時該語句依賴外部的查詢。
  • dependent subquery:和 DEPENDENT UNION 相對 UNION 一樣。

table

table 表示對應行正在訪問哪一個表,表名或者別名。

  • 關聯優化器會為查詢選擇關聯順序,左側深度優先。
  • 當 from 子句中有子查詢時,table 列是 <derivenN> 格式,表示的是當前查詢依賴 id=N 的查詢,於是先執行 id=N 的查詢。
  • 當有 union 時,UNION RESULT 的 table 列的值為 <union1, 2>,1 和 2 表示參與 union 的 select 行 id。

注意:MySQL 對待這些表和普通表一樣,但是這些“臨時表”是沒有任何索引的。


* type

這一列表示關聯類型或訪問類型,即 MySQL 決定如何查找表中的行,是較為重要的一個指標。

結果值從好到壞依次是:NULL > system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL

一般來說,得保證查詢至少達到 range 級別,最好能達到 ref 。

  • NULL:MySQL 能夠在優化階段分解查詢語句,在執行階段用不着再訪問表或索引。例如:在索引列中選取最小值,可以通過單獨查找索引值來完成,不需要在執行時訪問表。
mysql> explain select min(id) from film;
+----+-------------+-------+------+---------------+------+---------+------+------+------------------------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra                        |
+----+-------------+-------+------+---------------+------+---------+------+------+------------------------------+
|  1 | SIMPLE      | NULL  | NULL | NULL          | NULL | NULL    | NULL | NULL | Select tables optimized away |
+----+-------------+-------+------+---------------+------+---------+------+------+------------------------------+
  • const、system:MySQL 能對查詢的某部分進行優化並將其轉化成一個常量(可以看 show warnings 的結果),常用於 primary key 或 unique key 的所有列與常數比較時,因此表最多有一個匹配行,讀取 1 次,速度比較快。
mysql> explain extended select * from (select * from film where id = 1) tmp;
+----+-------------+------------+--------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table      | type   | possible_keys | key     | key_len | ref   | rows | filtered | Extra |
+----+-------------+------------+--------+---------------+---------+---------+-------+------+----------+-------+
|  1 | PRIMARY     | <derived2> | system | NULL          | NULL    | NULL    | NULL  |    1 |   100.00 | NULL  |
|  2 | DERIVED     | film       | const  | PRIMARY       | PRIMARY | 4       | const |    1 |   100.00 | NULL  |
+----+-------------+------------+--------+---------------+---------+---------+-------+------+----------+-------+

mysql> show warnings;
+-------+------+---------------------------------------------------------------+
| Level | Code | Message                                                       |
+-------+------+---------------------------------------------------------------+
| Note  | 1003 | /* select#1 */ select '1' AS `id`,'film1' AS `name` from dual |
+-------+------+---------------------------------------------------------------+
  • eq_ref:primary key 或 unique key 索引的所有部分被連接使用 ,最多只會返回一條符合條件的記錄。這可能是在 const 之外最好的聯接類型了,簡單的 select 查詢不會出現這種 type。
mysql> explain select * from film_actor left join film on film_actor.film_id = film.id;
+----+-------------+------------+--------+---------------+-------------------+---------+-------------------------+------+-------------+
| id | select_type | table      | type   | possible_keys | key               | key_len | ref                     | rows | Extra       |
+----+-------------+------------+--------+---------------+-------------------+---------+-------------------------+------+-------------+
|  1 | SIMPLE      | film_actor | index  | NULL          | idx_film_actor_id | 8       | NULL                    |    3 | Using index |
|  1 | SIMPLE      | film       | eq_ref | PRIMARY       | PRIMARY           | 4       | test.film_actor.film_id |    1 | NULL        |
+----+-------------+------------+--------+---------------+-------------------+---------+-------------------------+------+-------------+
  • ref:相比 eq_ref,不使用唯一索引,而是使用普通索引或者唯一索引的部分前綴。索引要和某個值相比較,可能會找到多個符合條件的行。
-- 1. 簡單 select 查詢,name 是普通索引(非唯一索引)
mysql> explain select * from film where name = "film1";
+----+-------------+-------+------+---------------+----------+---------+-------+------+--------------------------+
| id | select_type | table | type | possible_keys | key      | key_len | ref   | rows | Extra                    |
+----+-------------+-------+------+---------------+----------+---------+-------+------+--------------------------+
|  1 | SIMPLE      | film  | ref  | idx_name      | idx_name | 33      | const |    1 | Using where; Using index |
+----+-------------+-------+------+---------------+----------+---------+-------+------+--------------------------+

-- 2. 關聯表查詢,idx_film_actor_id 是 film_id 和 actor_id 的聯合索引,這里使用到了 film_actor 的左邊前綴 film_id 部分
mysql> explain select * from film left join film_actor on film.id = film_actor.film_id;
+----+-------------+------------+-------+-------------------+-------------------+---------+--------------+------+-------------+
| id | select_type | table      | type  | possible_keys     | key               | key_len | ref          | rows | Extra       |
+----+-------------+------------+-------+-------------------+-------------------+---------+--------------+------+-------------+
|  1 | SIMPLE      | film       | index | NULL              | idx_name          | 33      | NULL         |    3 | Using index |
|  1 | SIMPLE      | film_actor | ref   | idx_film_actor_id | idx_film_actor_id | 4       | test.film.id |    1 | Using index |
+----+-------------+------------+-------+-------------------+-------------------+---------+--------------+------+-------------+
  • ref_or_null:類似 ref,但是可以搜索值為 NULL 的行。
mysql> explain select * from film where name = "film1" or name is null;
+----+-------------+-------+-------------+---------------+----------+---------+-------+------+--------------------------+
| id | select_type | table | type        | possible_keys | key      | key_len | ref   | rows | Extra                    |
+----+-------------+-------+-------------+---------------+----------+---------+-------+------+--------------------------+
|  1 | SIMPLE      | film  | ref_or_null | idx_name      | idx_name | 33      | const |    2 | Using where; Using index |
+----+-------------+-------+-------------+---------------+----------+---------+-------+------+--------------------------+
  • index_merge:表示使用了索引合並的優化方法。例如下表:id 是主鍵,tenant_id 是普通索引。or 的時候沒有用 primary key,而是使用了 primary key(id) 和 tenant_id 索引。
mysql> explain select * from role where id = 11011 or tenant_id = 8888;
+----+-------------+-------+-------------+-----------------------+-----------------------+---------+------+------+-------------------------------------------------+
| id | select_type | table | type        | possible_keys         | key                   | key_len | ref  | rows | Extra                                           |
+----+-------------+-------+-------------+-----------------------+-----------------------+---------+------+------+-------------------------------------------------+
|  1 | SIMPLE      | role  | index_merge | PRIMARY,idx_tenant_id | PRIMARY,idx_tenant_id | 4,4     | NULL |  134 | Using union(PRIMARY,idx_tenant_id); Using where |
+----+-------------+-------+-------------+-----------------------+-----------------------+---------+------+------+-------------------------------------------------+
  • range:范圍掃描通常出現在 in()、between、>、<、>= 等操作中,表示使用一個索引來檢索給定范圍的行。一個良好的 SQL 效率至少要保證到該級別。
mysql> explain select * from actor where id > 1;
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type | table | type  | possible_keys | key     | key_len | ref  | rows | Extra       |
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
|  1 | SIMPLE      | actor | range | PRIMARY       | PRIMARY | 4       | NULL |    2 | Using where |
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
  • index:和 ALL 一樣,不同就是 MySQL 只需掃描索引樹,這通常比 ALL 快一些。
mysql> explain select count(*) from film;
+----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+
| id | select_type | table | type  | possible_keys | key      | key_len | ref  | rows | Extra       |
+----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+
|  1 | SIMPLE      | film  | index | NULL          | idx_name | 33      | NULL |    3 | Using index |
+----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+
  • ALL:全表掃描,意味着 MySQL 需要從頭到尾去查找所需要的行。通常情況下這需要增加索引來進行優化了。
mysql> explain select * from actor;
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
|  1 | SIMPLE      | actor | ALL  | NULL          | NULL | NULL    | NULL |    2 | NULL  |
+----+-------------+-------+------+---------------+------+---------+------+------+-------+

possible_keys

這一列顯示查詢可能使用哪些索引來查找,但是列出來的索引對於后續優化過程可能是沒有用上的。

explain 時可能出現 possible_keys 列有值,而 key 列顯示 NULL 的情況,通常是因為表中數據不多,MySQL 認為索引對此查詢幫助不大,於是選擇了全表查詢。

如果該列是 NULL,則表示沒有使用相關的索引。在這種情況下,可以通過檢查 where 子句看是否可以創造一個適當的索引來提高查詢性能,然后用 explain 查看效果。


* key

key 列顯示 MySQL 實際決定使用的鍵(索引)。

如果沒有使用索引,則該列是 NULL。如果想強制 MySQL 使用或忽視 possible_keys 列中的索引,可以在查詢中使用 force index、ignore index。


key_len

key_len 列顯示 MySQL 決定使用的索引長度(字節數),通過這個值可以算出具體使用了索引中的哪些列。

  • 如果值是 NULL,則表示長度為 NULL。
  • 在不損失精確性的情況下,索引長度越短越好 。

舉例來說,film_actor 的聯合索引 idx_film_actor_id 由 film_id 和 actor_id 這兩個 int 列組成,並且每個 int 是 4 字節。通過結果中的 key_len=4 可推斷出查詢使用了第一個列:film_id 列來執行索引查找。

mysql> explain select * from film_actor where film_id = 2;
+----+-------------+------------+------+-------------------+-------------------+---------+-------+------+-------------+
| id | select_type | table      | type | possible_keys     | key               | key_len | ref   | rows | Extra       |
+----+-------------+------------+------+-------------------+-------------------+---------+-------+------+-------------+
|  1 | SIMPLE      | film_actor | ref  | idx_film_actor_id | idx_film_actor_id | 4       | const |    1 | Using index |
+----+-------------+------------+------+-------------------+-------------------+---------+-------+------+-------------+

key_len 計算規則如下:

  • 字符串
    • char(n):n 字節長度
    • varchar(n):2 字節存儲字符串長度;如果是 utf-8,則長度為 3*n + 2
  • 數值類型
    • tinyint:1 字節
    • smallint:2 字節
    • int:4 字節
    • bigint:8 字節  
  • 時間類型 
    • date:3 字節
    • timestamp:4 字節
    • datetime:8 字節
  • 如果字段允許為 NULL,則需要 1 字節記錄是否為 NULL

索引最大長度是 768 字節,當字符串過長時,MySQL 會做一個類似左前綴索引的處理,將前半部分的字符提取出來做索引。


ref

這一列顯示了在 key 列記錄的索引中,表查找值所用到的列或常量,常見的有:const(常量)、func、NULL、字段名(例:film.id)。


* rows

rows 列顯示 MySQL 認為它執行查詢時必須檢查的行數。注意這是一個預估值。


* Extra

Extra 是 EXPLAIN 輸出中另外一個很重要的列,該列顯示 MySQL 在查詢過程中的一些詳細信息,MySQL 查詢優化器執行查詢的過程中對查詢計划的重要補充信息。

  • distinct: 一旦 MySQL 找到了與行相聯合匹配的行,就不再搜索了。
mysql> explain select distinct name from film left join film_actor on film.id = film_actor.film_id;
+----+-------------+------------+-------+-------------------+-------------------+---------+--------------+------+------------------------------+
| id | select_type | table      | type  | possible_keys     | key               | key_len | ref          | rows | Extra                        |
+----+-------------+------------+-------+-------------------+-------------------+---------+--------------+------+------------------------------+
|  1 | SIMPLE      | film       | index | idx_name          | idx_name          | 33      | NULL         |    3 | Using index; Using temporary |
|  1 | SIMPLE      | film_actor | ref   | idx_film_actor_id | idx_film_actor_id | 4       | test.film.id |    1 | Using index; Distinct        |
+----+-------------+------------+-------+-------------------+-------------------+---------+--------------+------+------------------------------+
  • Using index:這發生在對表的請求列都是索引的時候,不需要讀取數據文件,而從索引樹(索引文件)中即可獲得信息。這也是覆蓋索引的標識,是性能高的表現。這是 MySQL 服務層完成的,無需再回表查詢記錄。

    • 如果同時出現 using where,表明索引被用來執行索引鍵值的查找;
    • 沒有 using where,表明索引用來讀取數據而非執行查找動作。
      mysql> explain select id from film order by id;
      +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
      | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
      +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
      | 1 | SIMPLE | film | index | NULL | PRIMARY | 4 | NULL | 3 | Using index |
      +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
  • Using where:使用了 WHERE 從句來限制哪些行將與下一張表匹配或者是返回給用戶。注意:Extra 列出現 using where 表示 MySQL 服務器將存儲引擎返回服務層以后再應用 WHERE 條件過濾,符合就留下,不符合就丟棄。

mysql> explain select * from film where id > 1;
+----+-------------+-------+-------+---------------+----------+---------+------+------+--------------------------+
| id | select_type | table | type  | possible_keys | key      | key_len | ref  | rows | Extra                    |
+----+-------------+-------+-------+---------------+----------+---------+------+------+--------------------------+
|  1 | SIMPLE      | film  | index | PRIMARY       | idx_name | 33      | NULL |    3 | Using where; Using index |
+----+-------------+-------+-------+---------------+----------+---------+------+------+--------------------------+
  • Using temporary:表示需要用臨時表保存中間結果,常用於 GROUP BY 和 ORDER BY 操作中,一般看到它說明查詢需要優化了,就算避免不了臨時表的使用也要盡量避免硬盤臨時表的使用。
-- 1. actor.name 沒有索引,此時創建了張臨時表來 distinct
mysql> explain select distinct name from actor;
+----+-------------+-------+------+---------------+------+---------+------+------+-----------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra           |
+----+-------------+-------+------+---------------+------+---------+------+------+-----------------+
|  1 | SIMPLE      | actor | ALL  | NULL          | NULL | NULL    | NULL |    2 | Using temporary |
+----+-------------+-------+------+---------------+------+---------+------+------+-----------------+

-- 2. film.name 建立了 idx_name 索引,此時查詢時 extra 是 using index,沒有用臨時表
mysql> explain select distinct name from film;
+----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+
| id | select_type | table | type  | possible_keys | key      | key_len | ref  | rows | Extra       |
+----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+
|  1 | SIMPLE      | film  | index | idx_name      | idx_name | 33      | NULL |    3 | Using index |
+----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+
  • Using filesort:MySQL需要額外的一次傳遞,以找出如何按排序順序檢索行。通過根據聯接類型瀏覽所有行並為所有匹配 WHERE 子句的行保存排序關鍵字和行的指針來完成排序。然后關鍵字被排序,並按排序順序檢索行。
    • MySQL 有兩種方式可以生成有序的結果,通過排序操作或者使用索引。當 Extra 中出現了 Using filesort,說明查詢排序使用了前者。注意雖然叫 filesort 但並不說明就是用了文件來進行排序,只要可能,排序都是在內存里完成的。大部分情況下利用索引排序更快,所以一般這時也要考慮優化查詢了。
    • filesort 有兩種排序方式:
      • 對需要排序的記錄生成 <sort_key, rowid> 的元數據進行排序,該元數據僅包含排序字段和 rowid。排序完成后只有按字段排序的 rowid,因此還需要通過 rowid 進行回表操作獲取所需要的列的值,可能會導致大量的隨機 I/O 讀消耗。其解決方案是使用覆蓋索引
      • 對需要排序的記錄生成 <sort_key, additional_fields> 的元數據,該元數據包含排序字段和需要返回的所有列。排序完后不需要回表,但是元數據要比第一種方法長得多,需要更多的空間用於排序。其解決方案是:filesort 使用的算法是 QuickSort,即對需要排序的記錄生成元數據進行分塊排序,然后再使用 mergesort 方法合並塊。其中 filesort 可以使用的內存空間大小為參數 sort_buffer_size 的值,默認為 2M。當排序記錄太多導致 sort_buffer_size 不夠用時,MySQL 會使用臨時文件來存放各個分塊,然后各個分塊排序后再多次合並分塊最終全局完成排序。因此可以通過增大 sort_buffer_size 來解決 filesort 問題
-- 1. actor.name 未創建索引,會瀏覽 actor 整個表,保存排序關鍵字 name 和對應的 id,然后排序 name 並檢索行記錄
mysql> explain select * from actor order by name;
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra          |
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+
|  1 | SIMPLE      | actor | ALL  | NULL          | NULL | NULL    | NULL |    2 | Using filesort |
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+

-- 2. film.name 建立了 idx_name 索引,此時查詢時 extra 是 using index
mysql> explain select * from film order by name;
+----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+
| id | select_type | table | type  | possible_keys | key      | key_len | ref  | rows | Extra       |
+----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+
|  1 | SIMPLE      | film  | index | NULL          | idx_name | 33      | NULL |    3 | Using index |
+----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+
  • Not exists:MYSQL 優化了 LEFT JOIN,一旦它找到了匹配 LEFT JOIN 標准的行,就不再搜索了。

  • Using index condition:這是 MySQL 5.6 出來的新特性,叫做“索引條件推送"。簡單點說就是 MySQL 原來在索引上是不能執行如 like 這樣的操作的,但是現在可以了,這樣就減少了不必要的 I/O 操作,但是只能用在二級索引上。

  • Using join buffer:表示使用了連接緩存。

    • BlockNestedLoop,連接算法是塊嵌套循環連接。
    • BatchedKeyAccess,連接算法是批量索引連接。
  • impossible where:子句的值總是 false,不能用來獲取任何元組。

  • select tables optimized away:在沒有 GROUP BY 子句的情況下,基於索引優化 MIN/MAX 操作,或者對於 MyISAM 存儲引擎優化 COUNT(*) 操作,而不必等到執行階段再進行計算,即在查詢執行計划生成的階段就完成優化。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM