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 問題
。
- 對需要排序的記錄生成 <sort_key, rowid> 的元數據進行排序,該元數據僅包含排序字段和 rowid。排序完成后只有按字段排序的 rowid,因此還需要通過 rowid 進行回表操作獲取所需要的列的值,可能會導致大量的隨機 I/O 讀消耗。其
-- 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(*) 操作,而不必等到執行階段再進行計算,即在查詢執行計划生成的階段就完成優化。