mysql explain詳解

来源:https://www.cnblogs.com/butterfly100/archive/2018/01/15/8287569.html
-Advertisement-
Play Games

Explain簡介 本文主要講述如何通過 explain 命令獲取 select 語句的執行計劃,通過 explain 我們可以知道以下信息:表的讀取順序,數據讀取操作的類型,哪些索引可以使用,哪些索引實際使用了,表之間的引用,每張表有多少行被優化器查詢等信息。 下麵是使用 explain 的例子: ...


Explain簡介

本文主要講述如何通過 explain 命令獲取 select 語句的執行計劃,通過 explain 我們可以知道以下信息:表的讀取順序,數據讀取操作的類型,哪些索引可以使用,哪些索引實際使用了,表之間的引用,每張表有多少行被優化器查詢等信息。

下麵是使用 explain 的例子: 

在 select 語句之前增加 explain 關鍵字,MySQL 會在查詢上設置一個標記,執行查詢時,會返回執行計劃的信息,而不是執行這條SQL(如果 from 中包含子查詢,仍會執行該子查詢,將結果放入臨時表中)。

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  |
+----+-------------+-------+------+---------------+------+---------+------+------+-------+

在查詢中的每個表會輸出一行,如果有兩個表通過 join 連接查詢,那麼會輸出兩行。表的意義相當廣泛:可以是子查詢、一個 union 結果等。

explain 有兩個變種:

1)explain extended:會在 explain  的基礎上額外提供一些查詢優化的信息。緊隨其後通過 show warnings 命令可以 得到優化後的查詢語句,從而看出優化器優化了什麼。額外還有 filtered 列,是一個半分比的值,rows * filtered/100 可以估算出將要和 explain 中前一個表進行連接的行數(前一個表指 explain 中的id值比當前表id值小的表)。

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 |
+-------+------+--------------------------------------------------------------------------------+

2)explain partitions:相比 explain 多了個 partitions 欄位,如果查詢是基於分區表的話,會顯示查詢將訪問的分區。

explain 中的列

接下來我們將展示 explain 中每個列的信息。

1. id列

id列的編號是 select 的序列號,有幾個 select 就有幾個id,並且id的順序是按 select 出現的順序增長的。MySQL將 select 查詢分為簡單查詢和複雜查詢。複雜查詢分為三類:簡單子查詢、派生表(from語句中的子查詢)、union 查詢。

1)簡單子查詢

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 |
+----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+ 

2)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 查詢引用了這個臨時表

3)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。

2. select_type列

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

1)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  |
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+

2)primary:複雜查詢中最外層的 select

3)subquery:包含在 select 中的子查詢(不在 from 子句中)

4)derived:包含在 from 子句中的子查詢。MySQL會將結果存放在一個臨時表中,也稱為派生表(derived的英文含義)

用這個例子來瞭解 primary、subquery 和 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 |
+----+-------------+------------+--------+---------------+---------+---------+-------+------+-------------+ 

5)union:在 union 中的第二個和隨後的 select

6)union result:從 union 臨時表檢索結果的 select

用這個例子來瞭解 union 和 union result 類型:

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 |
+----+--------------+------------+------+---------------+------+---------+------+------+-----------------+

3. table列

這一列表示 explain 的一行正在訪問哪個表。

當 from 子句中有子查詢時,table列是 <derivenN> 格式,表示當前查詢依賴 id=N 的查詢,於是先執行 id=N 的查詢。當有 union 時,UNION RESULT 的 table 列的值為 <union1,2>,1和2表示參與 union 的 select 行id。

4. type列

這一列表示關聯類型或訪問類型,即MySQL決定如何查找表中的行。

依次從最優到最差分別為:system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL

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 ,> ,<, >= 等操作中。使用一個索引來檢索給定範圍的行。

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 |
+----+--------

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 處理MySQL的ibdata1文件過大問題 本人遇到一次在安裝zabbix監控的時候,yum安裝的MySQL資料庫,後面用了一段時間發現data目錄下的ibdata1的空間特別大,反而我的zabbix資料庫的空間很小,這樣的情況在後面備份zabbix資料庫的時候會很不方便,所以想著要怎麼解決下。 i ...
  • 今天客戶提了一個小需求,希望我能提供一條sql語句,幫助他對數據中 _field 這個欄位的值去重,並且保留其他欄位的數據。第一反應是select distinct,但這種語句在對某個欄位去重時,無法保留其他欄位,所以select distinct不成立。因為用戶對去重沒有要求,欄位值重覆時保留任意 ...
  • 一、簡介 MongoDB是一款強大、靈活、且易於擴展的通用型資料庫 1、易用性 1)MongoDB是一款面向文檔的資料庫,而不是關係型資料庫,因此而有著更好的擴展性。 2)通過在文檔中嵌入文檔和數組,面向文檔的方法能夠僅使用一條記錄來表現複雜的層級關係。 3)MongoDB沒有預定義模式(prede ...
  • 一、新增欄位 alert table user add( userName VARCHAR2(255 CHAR) ) ; 設置欄位不為空, 給出預設值 alert table user add( userName varchar2(255 CHAR) default '這是預設值' not null ...
  • 現觀察線上系統運行發現,線上某些業務查詢存在等待時間長問題,後核查發現,部分問題出現在對資料庫操作上Cost大部分時間,後根據網上各位前輩提供的優化技巧解決大部分問題,現寫下本篇文章,一來鞏固加深自己學習的優化技巧,二來方便正在為sql優化迷茫的猿友們提供一下思路和方法,共同進步,一起成長~ 1、現 ...
  • 很多人剛入門的時候分不清LEFT JOIN、RIGHT LOIN 和 INNER JOIN的區別,對它們的定義比較模糊,今天就簡單的介紹一下它們的區別,對於入門的人來說,應該能夠幫助你們理解。 left join(左聯接) 返回包括左表中的所有記錄和右表中聯結欄位相等的記錄 right join(右 ...
  • 進程體繫結構 -用戶進程:連接到Oracle DB的應用程式或工具 -資料庫進程 -伺服器進程:連接到Oracle實例,在用戶建立會話時啟動 -後臺進程:在啟動Oracle實例時啟動 -守護程式/應用程式進程 -網路監聽程式 -Grid infrastructe守護程式 專用伺服器,對於每個會話,運 ...
  • 本人在網上看了很多的博客資料,感覺大家都是在相互抄襲,我就自己根據自己的理解和經驗來總結一下吧; 首先觸發器的創建: 有使用Navicat的朋友可以在上面創建一個觸發器: (本人就是簡單的複製上面的sql ) CREATE TRIGGER `c_time` BEFORE INSERT ON `stu ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...