<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          MySQL執(zhí)行計劃Explain詳解

          共 40423字,需瀏覽 81分鐘

           ·

          2020-08-31 19:12

          ? 本文來源:http://8rr.co/S4B7

          大家都知道,mysql在執(zhí)行查詢的時候會進行查詢優(yōu)化。簡單來講就是執(zhí)行的時候先基于成本和規(guī)則優(yōu)化生成執(zhí)行計劃,然后再按照執(zhí)行計劃執(zhí)行查詢。本文主要介紹EXPLAIN各輸出項的含義,從而幫助大家更好的進行sql性能優(yōu)化

          本文主要內(nèi)容是根據(jù)掘金小冊《從根兒上理解 MySQL》整理而來。如想詳細了解,建議購買掘金小冊閱讀。

          我們可以在查詢語句前面加上EXPLAIN關(guān)鍵字來查看這個查詢的執(zhí)行計劃。例如

          mysql> EXPLAIN SELECT 1;+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------+| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra          |+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------+|  1 | SIMPLE      | NULL  | NULL       | NULL | NULL          | NULL | NULL    | NULL | NULL |     NULL | No tables used |+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------+1 row in set, 1 warning (0.01 sec)

          可以看到,執(zhí)行計劃包含很多輸出列,我們先簡單過一下各列的大致作用,后面再進行詳細講解。

          列名描述
          id在一個大的查詢語句中每個SELECT關(guān)鍵字都對應(yīng)一個唯一的id
          select_typeSELECT關(guān)鍵字對應(yīng)的那個查詢的類型
          table表名
          partitions匹配的分區(qū)信息
          type針對單表的訪問方法
          possible_keys可能用到的索引
          key實際上使用的索引
          key_len實際使用到的索引長度
          ref當使用索引列等值查詢時,與索引列進行等值匹配的對象信息
          rows預(yù)估的需要讀取的記錄條數(shù)
          filtered某個表經(jīng)過搜索條件過濾后剩余記錄條數(shù)的百分比
          Extra一些額外的信息

          前置相關(guān)知識點

          為了詳細了解執(zhí)行計劃各列含義,我們先得了解以下相關(guān)知識點。

          不相關(guān)子查詢

          如果子查詢可以單獨運行出結(jié)果,而不依賴于外層查詢,我們把這個子查詢稱之為不相關(guān)子查詢

          相關(guān)子查詢

          如果子查詢的執(zhí)行需要依賴于外層查詢的值,我們就把這個子查詢稱之為相關(guān)子查詢

          子查詢物化

          不直接將不相關(guān)子查詢的結(jié)果集當作外層查詢的參數(shù),而是將該結(jié)果集寫入一個臨時表(物化表)里。例如:

          SELECT * FROM s1 WHERE key1 IN (SELECT common_field FROM s2 WHERE key3 = 'a');

          假設(shè)子查詢物化表的名稱為materialized_table,該物化表存儲的子查詢結(jié)果集的列為m_val。子查詢物化之后可以將表s1和子查詢物化表materialized_table進行內(nèi)連接操作,然后獲取對應(yīng)的查詢結(jié)果。

          SELECT s1.* FROM s1 INNER JOIN materialized_table ON key1 = m_val;

          將子查詢轉(zhuǎn)換為semi-join

          將子查詢進行物化之后再執(zhí)行查詢都會有建立臨時表的成本,能不能不進行物化操作直接把子查詢轉(zhuǎn)換為連接呢?讓我們重新審視一下上邊的查詢語句:

          SELECT * FROM s1 WHERE key1 IN (SELECT common_field FROM s2 WHERE key3 = 'a');

          我們可以把這個查詢理解成:對于s1表中的某條記錄,如果我們能在s2表(準確的說是執(zhí)行完WHERE s2.key3 = 'a'之后的結(jié)果集)中找到一條或多條符合s2.common_field=s1.key1的記錄,那么該條s1表的記錄就會被加入到最終的結(jié)果集。這個過程其實和把s1s2兩個表連接起來的效果很像:

          SELECT s1.* FROM s1 INNER JOIN s2 ON s1.key1 = s2.common_field WHERE s2.key3 = 'a';

          這么做唯一的問題在于,對于s1表的某條記錄來說,如果s2表中有多條記錄滿足s1.key1 = s2.common_field這個條件,那么該記錄會被多次加入最終的結(jié)果集,因此二者不能認為是完全等價的,因此就有了semi-join(半連接)。將s1表和s2表進行半連接的意思就是:對于s1表的某條記錄來說,我們只關(guān)心在s2表中是否存在與之匹配的記錄,而不關(guān)心具體有多少條記錄與之匹配,最終的結(jié)果集中只保留s1表的記錄。當然semi-join是mysql內(nèi)部機制,無法直接用在sql語句中。

          semi-join實現(xiàn)機制

          Table pullout (子查詢中的表上拉)

          當子查詢的查詢列表處只有主鍵或者唯一索引列時,可以直接把子查詢中的表上拉到外層查詢的FROM子句中,并把子查詢中的搜索條件合并到外層查詢的搜索條件中,比如這個:

          SELECT * FROM s1  WHERE key2 IN (SELECT key2 FROM s2 WHERE key3 = 'a');

          由于key2列是s2表的唯一二級索引列,所以我們可以直接把s2表上拉到外層查詢的FROM子句中,并且把子查詢中的搜索條件合并到外層查詢的搜索條件中,實際上就是直接將子查詢優(yōu)化為連接查詢,上拉之后的查詢就是這樣的:

          SELECT s1.* FROM s1 INNER JOIN s2 ON s1.key2 = s2.key2 WHERE s2.key3 = 'a';
          DuplicateWeedout execution strategy (重復值消除)

          比如下面這個查詢語句:

          SELECT * FROM s1  WHERE key1 IN (SELECT common_field FROM s2 WHERE key3 = 'a');

          轉(zhuǎn)換為半連接查詢后,s1表中的某條記錄可能在s2表中有多條匹配的記錄,所以該條記錄可能多次被添加到最后的結(jié)果集中。為了消除重復,我們可以建立一個臨時表,比方說這個臨時表長這樣:

          CREATE TABLE tmp (    id PRIMARY KEY);

          這樣在執(zhí)行連接查詢的過程中,每當某條s1表中的記錄要加入結(jié)果集時,就首先把這條記錄的id值加入到這個臨時表里。這種使用臨時表消除semi-join結(jié)果集中的重復值的方式稱之為DuplicateWeedout

          LooseScan execution strategy (松散掃描)

          比如下面這個查詢語句:

          SELECT * FROM s1 WHERE key3 IN (SELECT key1 FROM s2 WHERE key1 > 'a' AND key1 < 'b');

          在子查詢中,對于s2表的訪問可以使用到key1列的索引,而恰好子查詢的查詢列表處就是key1列,這樣在將該查詢轉(zhuǎn)換為半連接查詢后,如果將s2作為驅(qū)動表執(zhí)行查詢的話,那么執(zhí)行過程就是這樣:

          如圖所示,在s2表的idx_key1索引中,值為'aa'的二級索引記錄一共有3條,那么只需要取第一條的值到s1表中查找s1.key3 = 'aa'的記錄,如果能在s1表中找到對應(yīng)的記錄,那么就把對應(yīng)的記錄加入到結(jié)果集。這種雖然是掃描索引,但只取值相同的記錄的第一條去做匹配操作的方式稱之為松散掃描。

          FirstMatch execution strategy (首次匹配)

          FirstMatch是一種最原始的半連接執(zhí)行方式,簡單來說就是說先取一條外層查詢的中的記錄,然后到子查詢的表中尋找符合匹配條件的記錄,如果能找到一條,則將該外層查詢的記錄放入最終的結(jié)果集并且停止查找更多匹配的記錄,如果找不到則把該外層查詢的記錄丟棄掉;然后再開始取下一條外層查詢中的記錄,重復上邊這個過程。

          執(zhí)行計劃詳解

          為了詳細解釋執(zhí)行計劃各列含義,先建2張示例表s1s2,它們的表結(jié)構(gòu)完全一樣。

          CREATE TABLE s1 (    id INT NOT NULL AUTO_INCREMENT,    key1 VARCHAR(100),    key2 INT,    key3 VARCHAR(100),    key_part1 VARCHAR(100),    key_part2 VARCHAR(100),    key_part3 VARCHAR(100),    common_field VARCHAR(100),    PRIMARY KEY (id),    KEY idx_key1 (key1),    UNIQUE KEY idx_key2 (key2),    KEY idx_key3 (key3),    KEY idx_key_part(key_part1, key_part2, key_part3)) Engine=InnoDB CHARSET=utf8;

          table

          不論我們的查詢語句有多復雜,里邊兒包含了多少個表,到最后也是需要對每個表進行單表訪問的,因此EXPLAIN語句輸出的每條記錄都對應(yīng)著某個單表的訪問方法。其中的table列代表的就是該表的表名。比如:

          mysql> EXPLAIN SELECT * FROM s1;+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------+| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra |+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------+|  1 | SIMPLE      | s1    | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 9688 |   100.00 | NULL  |+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------+1 row in set, 1 warning (0.00 sec)

          上面的查詢只涉及單表查詢,因此EXPLAIN只輸出了一條記錄。table列的值是s1,表示該條記錄描述了s1表的訪問方法

          下邊我們看一下一個連接查詢的執(zhí)行計劃:

          mysql> EXPLAIN SELECT * FROM s1 INNER JOIN s2;+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+---------------------------------------+| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra                                 |+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+---------------------------------------+|  1 | SIMPLE      | s1    | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 9688 |   100.00 | NULL                                  ||  1 | SIMPLE      | s2    | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 9954 |   100.00 | Using join buffer (Block Nested Loop) |+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+---------------------------------------+2 rows in set, 1 warning (0.01 sec)

          可以看到,EXPLAIN只輸出了兩條記錄。table列的值是s1s2,分別表示了s1表和s2表的訪問方法

          id

          大家都知道,查詢語句中一般都會包含一個或多個select關(guān)鍵字。可以簡單認為,查詢語句每出現(xiàn)一個select關(guān)鍵字,執(zhí)行計劃中就會有一個對應(yīng)的id值。比如下邊這個查詢中只有一個SELECT關(guān)鍵字:

          mysql> EXPLAIN SELECT * FROM s1 WHERE key1 = 'a';+----+-------------+-------+------------+------+---------------+----------+---------+-------+------+----------+-------+| id | select_type | table | partitions | type | possible_keys | key      | key_len | ref   | rows | filtered | Extra |+----+-------------+-------+------------+------+---------------+----------+---------+-------+------+----------+-------+|  1 | SIMPLE      | s1    | NULL       | ref  | idx_key1      | idx_key1 | 303     | const |    8 |   100.00 | NULL  |+----+-------------+-------+------------+------+---------------+----------+---------+-------+------+----------+-------+1 row in set, 1 warning (0.03 sec)

          對于連接查詢來說,一個select往往是對多張表進行查詢的,所以在執(zhí)行計劃中就會有多條記錄,但是它們的id都是一樣的。其中,出現(xiàn)在前邊的表是驅(qū)動表,出現(xiàn)在后邊的表是被驅(qū)動表。

          mysql> EXPLAIN SELECT * FROM s1 INNER JOIN s2;+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+---------------------------------------+| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra                                 |+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+---------------------------------------+|  1 | SIMPLE      | s1    | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 9688 |   100.00 | NULL                                  ||  1 | SIMPLE      | s2    | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 9954 |   100.00 | Using join buffer (Block Nested Loop) |+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+---------------------------------------+2 rows in set, 1 warning (0.01 sec)

          對于子查詢來說,就可能包含多個select關(guān)鍵字,每個select關(guān)鍵字都會對應(yīng)一個唯一的id值

          mysql> EXPLAIN SELECT * FROM s1 WHERE key1 IN (SELECT key1 FROM s2) OR key3 = 'a';+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-------------+| id | select_type | table | partitions | type  | possible_keys | key      | key_len | ref  | rows | filtered | Extra       |+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-------------+|  1 | PRIMARY     | s1    | NULL       | ALL   | idx_key3      | NULL     | NULL    | NULL | 9688 |   100.00 | Using where ||  2 | SUBQUERY    | s2    | NULL       | index | idx_key1      | idx_key1 | 303     | NULL | 9954 |   100.00 | Using index |+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-------------+2 rows in set, 1 warning (0.02 sec)

          但是還有一點需要注意:查詢優(yōu)化器可能對涉及子查詢的查詢語句進行重寫,從而轉(zhuǎn)換為連接查詢。此時執(zhí)行計劃的id值就是一樣的了。

          對于包含union關(guān)鍵字的查詢來說,除了每個select關(guān)鍵字對應(yīng)一個id值,還會包含一個id值為NULL的記錄。這條記錄主要用來表示將兩次查詢的結(jié)果集進行去重的(union all因為不需要去重,所以沒有這條記錄)。

          mysql> EXPLAIN SELECT * FROM s1  UNION SELECT * FROM s2;+----+--------------+------------+------------+------+---------------+------+---------+------+------+----------+-----------------+| id | select_type  | table      | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra           |+----+--------------+------------+------------+------+---------------+------+---------+------+------+----------+-----------------+|  1 | PRIMARY      | s1         | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 9688 |   100.00 | NULL            ||  2 | UNION        | s2         | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 9954 |   100.00 | NULL            || NULL | UNION RESULT |  | NULL       | ALL  | NULL          | NULL | NULL    | NULL | NULL |     NULL | Using temporary |+----+--------------+------------+------------+------+---------------+------+---------+------+------+----------+-----------------+3 rows in set, 1 warning (0.00 sec)

          select_type

          我們已經(jīng)知道,每一個select關(guān)鍵字都代表一次小查詢,而select_type屬性就是用來描述當前這個小查詢的含義的。select_type屬性含義(直接用官網(wǎng)英文表示)如下:

          名稱描述
          SIMPLESimple SELECT (not using UNION or subqueries)
          PRIMARYOutermost SELECT
          UNIONSecond or later SELECT statement in a UNION
          UNION RESULTResult of a UNION
          SUBQUERYFirst SELECT in subquery
          DEPENDENT SUBQUERYFirst SELECT in subquery, dependent on outer query
          DEPENDENT UNIONSecond or later SELECT statement in a UNION, dependent on outer query
          DERIVEDDerived table
          MATERIALIZEDMaterialized subquery
          UNCACHEABLE SUBQUERYA subquery for which the result cannot be cached and must be re-evaluated for each row of the outer query
          UNCACHEABLE UNIONThe second or later select in a UNION that belongs to an uncacheable subquery (see UNCACHEABLE SUBQUERY)

          SIMPLE

          查詢語句中不包含UNION或者子查詢的查詢都算作是SIMPLE類型,比如常見的單表查詢和連接查詢等。

          PRIMARY

          對于包含UNIONUNION ALL或者子查詢的大查詢來說,它是由幾個小查詢組成的,其中最左邊的那個查詢的select_type值就是PRIMARY,比方說:

          mysql> EXPLAIN SELECT * FROM s1 UNION SELECT * FROM s2;+----+--------------+------------+------------+------+---------------+------+---------+------+------+----------+-----------------+| id | select_type  | table      | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra           |+----+--------------+------------+------------+------+---------------+------+---------+------+------+----------+-----------------+|  1 | PRIMARY      | s1         | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 9688 |   100.00 | NULL            ||  2 | UNION        | s2         | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 9954 |   100.00 | NULL            || NULL | UNION RESULT |  | NULL       | ALL  | NULL          | NULL | NULL    | NULL | NULL |     NULL | Using temporary |+----+--------------+------------+------------+------+---------------+------+---------+------+------+----------+-----------------+3 rows in set, 1 warning (0.00 sec)

          UNION

          對于包含UNION或者UNION ALL的大查詢來說,它是由幾個小查詢組成的,其中除了最左邊的那個小查詢以外,其余的小查詢的select_type值就是UNION

          UNION RESULT

          MySQL選擇使用臨時表來完成UNION查詢的去重工作,針對該臨時表的查詢的select_type就是UNION RESULT

          SUBQUERY

          如果包含子查詢的查詢語句不能夠轉(zhuǎn)為對應(yīng)的semi-join的形式,并且該子查詢是不相關(guān)子查詢,并且查詢優(yōu)化器決定采用將該子查詢物化的方案來執(zhí)行該子查詢時,該子查詢的第一個SELECT關(guān)鍵字代表的那個查詢的select_type就是SUBQUERY,比如下邊這個查詢:

          mysql> EXPLAIN SELECT * FROM s1 WHERE key1 IN (SELECT key1 FROM s2) OR key3 = 'a';+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-------------+| id | select_type | table | partitions | type  | possible_keys | key      | key_len | ref  | rows | filtered | Extra       |+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-------------+|  1 | PRIMARY     | s1    | NULL       | ALL   | idx_key3      | NULL     | NULL    | NULL | 9688 |   100.00 | Using where ||  2 | SUBQUERY    | s2    | NULL       | index | idx_key1      | idx_key1 | 303     | NULL | 9954 |   100.00 | Using index |+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-------------+2 rows in set, 1 warning (0.00 sec)

          DEPENDENT SUBQUERY

          如果包含子查詢的查詢語句不能夠轉(zhuǎn)為對應(yīng)的semi-join的形式,并且該子查詢是相關(guān)子查詢,則該子查詢的第一個SELECT關(guān)鍵字代表的那個查詢的select_type就是DEPENDENT SUBQUERY,比如下邊這個查詢:

          mysql> EXPLAIN SELECT * FROM s1 WHERE key1 IN (SELECT key1 FROM s2 WHERE s1.key2 = s2.key2) OR key3 = 'a';+----+--------------------+-------+------------+------+-------------------+----------+---------+-------------------+------+----------+-------------+| id | select_type        | table | partitions | type | possible_keys     | key      | key_len | ref               | rows | filtered | Extra       |+----+--------------------+-------+------------+------+-------------------+----------+---------+-------------------+------+----------+-------------+|  1 | PRIMARY            | s1    | NULL       | ALL  | idx_key3          | NULL     | NULL    | NULL              | 9688 |   100.00 | Using where ||  2 | DEPENDENT SUBQUERY | s2    | NULL       | ref  | idx_key2,idx_key1 | idx_key2 | 5       | xiaohaizi.s1.key2 |    1 |    10.00 | Using where |+----+--------------------+-------+------------+------+-------------------+----------+---------+-------------------+------+----------+-------------+2 rows in set, 2 warnings (0.00 sec)

          DEPENDENT UNION

          在包含UNION或者UNION ALL的大查詢中,如果各個小查詢都依賴于外層查詢的話,那除了最左邊的那個小查詢之外,其余的小查詢的select_type的值就是DEPENDENT UNION

          mysql> EXPLAIN SELECT * FROM s1 WHERE key1 IN (SELECT key1 FROM s2 WHERE key1 = 'a' UNION SELECT key1 FROM s1 WHERE key1 = 'b');+----+--------------------+------------+------------+------+---------------+----------+---------+-------+------+----------+--------------------------+| id | select_type        | table      | partitions | type | possible_keys | key      | key_len | ref   | rows | filtered | Extra                    |+----+--------------------+------------+------------+------+---------------+----------+---------+-------+------+----------+--------------------------+|  1 | PRIMARY            | s1         | NULL       | ALL  | NULL          | NULL     | NULL    | NULL  | 9688 |   100.00 | Using where              ||  2 | DEPENDENT SUBQUERY | s2         | NULL       | ref  | idx_key1      | idx_key1 | 303     | const |   12 |   100.00 | Using where; Using index ||  3 | DEPENDENT UNION    | s1         | NULL       | ref  | idx_key1      | idx_key1 | 303     | const |    8 |   100.00 | Using where; Using index || NULL | UNION RESULT       |  | NULL       | ALL  | NULL          | NULL     | NULL    | NULL  | NULL |     NULL | Using temporary          |+----+--------------------+------------+------------+------+---------------+----------+---------+-------+------+----------+--------------------------+4 rows in set, 1 warning (0.03 sec)

          DERIVED

          對于采用物化的方式執(zhí)行的包含派生表的查詢,該派生表對應(yīng)的子查詢的select_type就是DERIVED,比方說下邊這個查詢:

          mysql> EXPLAIN SELECT * FROM (SELECT key1, count(*) as c FROM s1 GROUP BY key1) AS derived_s1 where c > 1;+----+-------------+------------+------------+-------+---------------+----------+---------+------+------+----------+-------------+| id | select_type | table      | partitions | type  | possible_keys | key      | key_len | ref  | rows | filtered | Extra       |+----+-------------+------------+------------+-------+---------------+----------+---------+------+------+----------+-------------+|  1 | PRIMARY     |  | NULL       | ALL   | NULL          | NULL     | NULL    | NULL | 9688 |    33.33 | Using where ||  2 | DERIVED     | s1         | NULL       | index | idx_key1      | idx_key1 | 303     | NULL | 9688 |   100.00 | Using index |+----+-------------+------------+------------+-------+---------------+----------+---------+------+------+----------+-------------+2 rows in set, 1 warning (0.00 sec)

          MATERIALIZED

          當查詢優(yōu)化器在執(zhí)行包含子查詢的語句時,選擇將子查詢物化之后與外層查詢進行連接查詢時,該子查詢對應(yīng)的select_type屬性就是MATERIALIZED,比如下邊這個查詢:

          mysql> EXPLAIN SELECT * FROM s1 WHERE key1 IN (SELECT key1 FROM s2);+----+--------------+-------------+------------+--------+---------------+------------+---------+-------------------+------+----------+-------------+| id | select_type  | table       | partitions | type   | possible_keys | key        | key_len | ref               | rows | filtered | Extra       |+----+--------------+-------------+------------+--------+---------------+------------+---------+-------------------+------+----------+-------------+|  1 | SIMPLE       | s1          | NULL       | ALL    | idx_key1      | NULL       | NULL    | NULL              | 9688 |   100.00 | Using where ||  1 | SIMPLE       |  | NULL       | eq_ref |     |  | 303     | xiaohaizi.s1.key1 |    1 |   100.00 | NULL        ||  2 | MATERIALIZED | s2          | NULL       | index  | idx_key1      | idx_key1   | 303     | NULL              | 9954 |   100.00 | Using index |+----+--------------+-------------+------------+--------+---------------+------------+---------+-------------------+------+----------+-------------+3 rows in set, 1 warning (0.01 sec)

          type

          上面提到過,執(zhí)行計劃的一條記錄就代表了對一張表的訪問方法,其中的type列就是用描述訪問方法的。完整的訪問方法如下:systemconsteq_refreffulltextref_or_nullindex_mergeunique_subqueryindex_subqueryrangeindexALL

          system

          當表中只有一條記錄并且該表使用的存儲引擎的統(tǒng)計數(shù)據(jù)是精確的,比如MyISAMMemory,那么對該表的訪問方法就是system

          const

          根據(jù)主鍵或者唯一二級索引列與常數(shù)進行等值匹配時,對單表的訪問方法就是const

          mysql> EXPLAIN SELECT * FROM s1 WHERE id = 5;+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+| id | select_type | table | partitions | type  | possible_keys | key     | key_len | ref   | rows | filtered | Extra |+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+|  1 | SIMPLE      | s1    | NULL       | const | PRIMARY       | PRIMARY | 4       | const |    1 |   100.00 | NULL  |+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+1 row in set, 1 warning (0.01 sec)

          eq_ref

          在連接查詢時,如果被驅(qū)動表是通過主鍵或者唯一二級索引列等值匹配的方式進行訪問的(如果該主鍵或者唯一二級索引是聯(lián)合索引的話,所有的索引列都必須進行等值比較),則對該被驅(qū)動表的訪問方法就是eq_ref

          mysql> EXPLAIN SELECT * FROM s1 INNER JOIN s2 ON s1.id = s2.id;+----+-------------+-------+------------+--------+---------------+---------+---------+-----------------+------+----------+-------+| id | select_type | table | partitions | type   | possible_keys | key     | key_len | ref             | rows | filtered | Extra |+----+-------------+-------+------------+--------+---------------+---------+---------+-----------------+------+----------+-------+|  1 | SIMPLE      | s1    | NULL       | ALL    | PRIMARY       | NULL    | NULL    | NULL            | 9688 |   100.00 | NULL  ||  1 | SIMPLE      | s2    | NULL       | eq_ref | PRIMARY       | PRIMARY | 4       | xiaohaizi.s1.id |    1 |   100.00 | NULL  |+----+-------------+-------+------------+--------+---------------+---------+---------+-----------------+------+----------+-------+2 rows in set, 1 warning (0.01 sec)

          ref

          當通過普通的二級索引列與常量進行等值匹配時來查詢某個表,那么對該表的訪問方法就可能是ref

          mysql> EXPLAIN SELECT * FROM s1 WHERE key1 = 'a';+----+-------------+-------+------------+------+---------------+----------+---------+-------+------+----------+-------+| id | select_type | table | partitions | type | possible_keys | key      | key_len | ref   | rows | filtered | Extra |+----+-------------+-------+------------+------+---------------+----------+---------+-------+------+----------+-------+|  1 | SIMPLE      | s1    | NULL       | ref  | idx_key1      | idx_key1 | 303     | const |    8 |   100.00 | NULL  |+----+-------------+-------+------------+------+---------------+----------+---------+-------+------+----------+-------+1 row in set, 1 warning (0.04 sec)

          ref_or_null

          當對普通二級索引進行等值匹配查詢,該索引列的值也可以是NULL值時,那么對該表的訪問方法就可能是ref_or_null

          mysql> EXPLAIN SELECT * FROM s1 WHERE key1 = 'a' OR key1 IS NULL;+----+-------------+-------+------------+-------------+---------------+----------+---------+-------+------+----------+-----------------------+| id | select_type | table | partitions | type        | possible_keys | key      | key_len | ref   | rows | filtered | Extra                 |+----+-------------+-------+------------+-------------+---------------+----------+---------+-------+------+----------+-----------------------+|  1 | SIMPLE      | s1    | NULL       | ref_or_null | idx_key1      | idx_key1 | 303     | const |    9 |   100.00 | Using index condition |+----+-------------+-------+------------+-------------+---------------+----------+---------+-------+------+----------+-----------------------+1 row in set, 1 warning (0.01 sec)

          index_merge

          一般情況下對于某個表的查詢只能使用到一個索引,但是某些場景下也可能使用索引合并,此時的type就是index_merge

          mysql> EXPLAIN SELECT * FROM s1 WHERE key1 = 'a' OR key3 = 'a';+----+-------------+-------+------------+-------------+-------------------+-------------------+---------+------+------+----------+---------------------------------------------+| id | select_type | table | partitions | type        | possible_keys     | key               | key_len | ref  | rows | filtered | Extra                                       |+----+-------------+-------+------------+-------------+-------------------+-------------------+---------+------+------+----------+---------------------------------------------+|  1 | SIMPLE      | s1    | NULL       | index_merge | idx_key1,idx_key3 | idx_key1,idx_key3 | 303,303 | NULL |   14 |   100.00 | Using union(idx_key1,idx_key3); Using where |+----+-------------+-------+------------+-------------+-------------------+-------------------+---------+------+------+----------+---------------------------------------------+1 row in set, 1 warning (0.01 sec)
          索引合并

          一般情況下,執(zhí)行一個查詢最多只會用到一個索引。但是在特殊情況下也可能會使用多個二級索引,使用這種方式執(zhí)行的查詢稱為index_merge。具體的索引合并算法有下邊三種。

          • Intersection合并 Intersection翻譯過來的意思是交集。這里是說某個查詢可以使用多個二級索引,將從多個二級索引中查詢到的結(jié)果取交集,比方說下邊這個查詢:

            SELECT * FROM single_table WHERE key1 = 'a' AND key3 = 'b';
          • Union合并 我們在寫查詢語句時經(jīng)常想把既符合某個搜索條件的記錄取出來,也把符合另外的某個搜索條件的記錄取出來,我們說這些不同的搜索條件之間是OR關(guān)系。比如:

            SELECT * FROM single_table WHERE key1 = 'a' OR key3 = 'b'

            Intersection是交集的意思,這適用于使用不同索引的搜索條件之間使用AND連接起來的情況;Union是并集的意思,適用于使用不同索引的搜索條件之間使用OR連接起來的情況。

          • Sort-Union合并 Union索引合并的使用條件太苛刻,必須保證各個二級索引列在進行等值匹配的條件下才可能被用到,比方說下邊這個查詢就無法使用到Union索引合并:

            SELECT * FROM single_table WHERE key1 < 'a' OR key3 > 'z'

            我們把上述這種先按照二級索引記錄的主鍵值進行排序,之后按照Union索引合并方式執(zhí)行的方式稱之為Sort-Union索引合并,很顯然,這種Sort-Union索引合并比單純的Union索引合并多了一步對二級索引記錄的主鍵值排序的過程。

          unique_subquery

          類似于兩表連接中被驅(qū)動表的eq_ref訪問方法,unique_subquery是針對在一些包含IN子查詢的查詢語句中,如果查詢優(yōu)化器決定將IN子查詢轉(zhuǎn)換為EXISTS子查詢,而且子查詢可以使用到主鍵進行等值匹配的話,那么該子查詢執(zhí)行計劃的type列的值就是unique_subquery,比如下邊的這個查詢語句:

          mysql> EXPLAIN SELECT * FROM s1 WHERE key2 IN (SELECT id FROM s2 where s1.key1 = s2.key1) OR key3 = 'a';+----+--------------------+-------+------------+-----------------+------------------+---------+---------+------+------+----------+-------------+| id | select_type        | table | partitions | type            | possible_keys    | key     | key_len | ref  | rows | filtered | Extra       |+----+--------------------+-------+------------+-----------------+------------------+---------+---------+------+------+----------+-------------+|  1 | PRIMARY            | s1    | NULL       | ALL             | idx_key3         | NULL    | NULL    | NULL | 9688 |   100.00 | Using where ||  2 | DEPENDENT SUBQUERY | s2    | NULL       | unique_subquery | PRIMARY,idx_key1 | PRIMARY | 4       | func |    1 |    10.00 | Using where |+----+--------------------+-------+------------+-----------------+------------------+---------+---------+------+------+----------+-------------+2 rows in set, 2 warnings (0.00 sec)

          index_subquery

          index_subqueryunique_subquery類似,只不過訪問子查詢中的表時使用的是普通的索引,比如這樣:

          mysql> EXPLAIN SELECT * FROM s1 WHERE common_field IN (SELECT key3 FROM s2 where s1.key1 = s2.key1) OR key3 = 'a';+----+--------------------+-------+------------+----------------+-------------------+----------+---------+------+------+----------+-------------+| id | select_type        | table | partitions | type           | possible_keys     | key      | key_len | ref  | rows | filtered | Extra       |+----+--------------------+-------+------------+----------------+-------------------+----------+---------+------+------+----------+-------------+|  1 | PRIMARY            | s1    | NULL       | ALL            | idx_key3          | NULL     | NULL    | NULL | 9688 |   100.00 | Using where ||  2 | DEPENDENT SUBQUERY | s2    | NULL       | index_subquery | idx_key1,idx_key3 | idx_key3 | 303     | func |    1 |    10.00 | Using where |+----+--------------------+-------+------------+----------------+-------------------+----------+---------+------+------+----------+-------------+2 rows in set, 2 warnings (0.01 sec)

          range

          如果使用索引獲取某些范圍區(qū)間的記錄,那么就可能使用到range訪問方法,比如下邊的這個查詢:

          mysql> EXPLAIN SELECT * FROM s1 WHERE key1 IN ('a', 'b', 'c');+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-----------------------+| id | select_type | table | partitions | type  | possible_keys | key      | key_len | ref  | rows | filtered | Extra                 |+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-----------------------+|  1 | SIMPLE      | s1    | NULL       | range | idx_key1      | idx_key1 | 303     | NULL |   27 |   100.00 | Using index condition |+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-----------------------+1 row in set, 1 warning (0.01 sec)

          index

          需要掃描全部的索引記錄時,該表的訪問方法就是index

          mysql> EXPLAIN SELECT key_part2 FROM s1 WHERE key_part3 = 'a';+----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+--------------------------+| id | select_type | table | partitions | type  | possible_keys | key          | key_len | ref  | rows | filtered | Extra                    |+----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+--------------------------+|  1 | SIMPLE      | s1    | NULL       | index | NULL          | idx_key_part | 909     | NULL | 9688 |    10.00 | Using where; Using index |+----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+--------------------------+1 row in set, 1 warning (0.00 sec)

          ALL

          全表掃描

          possible_keys和key

          possible_keys列表示在某個查詢語句中,對某個表執(zhí)行單表查詢時可能用到的索引有哪些,key列表示實際用到的索引有哪些,比方說下邊這個查詢:

          mysql> EXPLAIN SELECT * FROM s1 WHERE key1 > 'z' AND key3 = 'a';+----+-------------+-------+------------+------+-------------------+----------+---------+-------+------+----------+-------------+| id | select_type | table | partitions | type | possible_keys     | key      | key_len | ref   | rows | filtered | Extra       |+----+-------------+-------+------------+------+-------------------+----------+---------+-------+------+----------+-------------+|  1 | SIMPLE      | s1    | NULL       | ref  | idx_key1,idx_key3 | idx_key3 | 303     | const |    6 |     2.75 | Using where |+----+-------------+-------+------------+------+-------------------+----------+---------+-------+------+----------+-------------+1 row in set, 1 warning (0.01 sec)

          key_len

          key_len列表示當優(yōu)化器決定使用某個索引執(zhí)行查詢時,該索引記錄的最大長度,它是由這三個部分構(gòu)成的:

          • 對于使用固定長度類型的索引列來說,它實際占用的存儲空間的最大長度就是該固定值;對于指定字符集是變長類型的索引列來說,比如某個索引列的類型是VARCHAR(100),使用的字符集是utf8,那么該列實際占用的最大存儲空間就是100 × 3 = 300個字節(jié)。
          • 如果該索引列可以存儲NULL值,則key_len比不可以存儲NULL值時多1個字節(jié)。
          • 對于變長字段來說,都會有2個字節(jié)的空間來存儲該變長列的實際長度。

          ref

          當使用索引列等值匹配的條件去執(zhí)行查詢時,也就是在訪問方法是consteq_refrefref_or_nullunique_subqueryindex_subquery其中之一時,ref列展示的就是與索引列作等值匹配的具體信息,比如只是一個常數(shù)或者是某個列。大家看下邊這個查詢:

          mysql> EXPLAIN SELECT * FROM s1 WHERE key1 = 'a';+----+-------------+-------+------------+------+---------------+----------+---------+-------+------+----------+-------+| id | select_type | table | partitions | type | possible_keys | key      | key_len | ref   | rows | filtered | Extra |+----+-------------+-------+------------+------+---------------+----------+---------+-------+------+----------+-------+|  1 | SIMPLE      | s1    | NULL       | ref  | idx_key1      | idx_key1 | 303     | const |    8 |   100.00 | NULL  |+----+-------------+-------+------------+------+---------------+----------+---------+-------+------+----------+-------+1 row in set, 1 warning (0.01 sec)

          rows

          如果查詢優(yōu)化器決定使用全表掃描的方式對某個表執(zhí)行查詢時,執(zhí)行計劃的rows列就代表預(yù)計需要掃描的行數(shù),如果使用索引來執(zhí)行查詢時,執(zhí)行計劃的rows列就代表預(yù)計掃描的索引記錄行數(shù)。比如下邊這個查詢:

          mysql> EXPLAIN SELECT * FROM s1 WHERE key1 > 'z';+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-----------------------+| id | select_type | table | partitions | type  | possible_keys | key      | key_len | ref  | rows | filtered | Extra                 |+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-----------------------+|  1 | SIMPLE      | s1    | NULL       | range | idx_key1      | idx_key1 | 303     | NULL |  266 |   100.00 | Using index condition |+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-----------------------+1 row in set, 1 warning (0.00 sec)

          filtered

          我們更關(guān)注在連接查詢中驅(qū)動表對應(yīng)的執(zhí)行計劃記錄的filtered值,因為這直接影響了驅(qū)動表的扇出值。在rows樣的情況下,filtered越大,扇出值越小,效率可能也越高。比如:

          mysql> EXPLAIN SELECT * FROM s1 INNER JOIN s2 ON s1.key1 = s2.key1 WHERE s1.common_field = 'a';+----+-------------+-------+------------+------+---------------+----------+---------+-------------------+------+----------+-------------+| id | select_type | table | partitions | type | possible_keys | key      | key_len | ref               | rows | filtered | Extra       |+----+-------------+-------+------------+------+---------------+----------+---------+-------------------+------+----------+-------------+|  1 | SIMPLE      | s1    | NULL       | ALL  | idx_key1      | NULL     | NULL    | NULL              | 9688 |    10.00 | Using where ||  1 | SIMPLE      | s2    | NULL       | ref  | idx_key1      | idx_key1 | 303     | xiaohaizi.s1.key1 |    1 |   100.00 | NULL        |+----+-------------+-------+------------+------+---------------+----------+---------+-------------------+------+----------+-------------+2 rows in set, 1 warning (0.00 sec)

          從執(zhí)行計劃中可以看出來,查詢優(yōu)化器打算把s1當作驅(qū)動表,s2當作被驅(qū)動表。我們可以看到驅(qū)動表s1表的執(zhí)行計劃的rows列為9688, filtered列為10.00,這意味著驅(qū)動表s1的扇出值就是9688 × 10.00% = 968.8,這說明還要對被驅(qū)動表執(zhí)行大約968次查詢。

          Extra

          Extra是用來說明一些額信息的,從而幫助我們更加準確的理解查詢。下面我們挑幾個比較常見的進行介紹。

          No tables used

          當查詢語句中沒有from字句時會出現(xiàn)No tables used

          Impossible WHERE

          當查詢語句中的where字句永遠為false時會出現(xiàn)Impossible WHERE

          No matching min/max row

          當查詢列表有min()或者max()聚集函數(shù),但是沒有匹配到對應(yīng)的記錄時會出現(xiàn)No matching min/max row

          Using index

          當使用索引覆蓋的時候,會出現(xiàn)Using index

          Using index condition

          如果查詢的執(zhí)行過程中使用了索引條件下推(Index Condition Pushdown),就會出現(xiàn)Using index condition。例如:

          SELECT * FROM s1 WHERE key1 > 'z' AND key1 LIKE '%a';

          1、先根據(jù)key1 > 'z'這個條件,定位到二級索引idx_key1中對應(yīng)的二級索引記錄。2、先不回表,而是檢測是否滿足key1 LIKE '%a'條件,最后再將滿足條件的二級索引記錄回表。

          Using where

          當使用全表掃描執(zhí)行查詢時,如果查詢語句包含where條件,就會出現(xiàn)Using where。當使用索引訪問執(zhí)行查詢時,如果where字句包含非索引列字段,也會出現(xiàn)Using where

          Using join buffer (Block Nested Loop)

          在連接查詢執(zhí)行過程中,當被驅(qū)動表不能有效的利用索引加快訪問速度,MySQL一般會為其分配一塊名叫join buffer的內(nèi)存塊來加快查詢速度,也就是我們所講的基于塊的嵌套循環(huán)算法,比如下邊這個查詢語句:

          mysql> EXPLAIN SELECT * FROM s1 INNER JOIN s2 ON s1.common_field = s2.common_field;+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra                                              |+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+|  1 | SIMPLE      | s1    | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 9688 |   100.00 | NULL                                               ||  1 | SIMPLE      | s2    | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 9954 |    10.00 | Using where; Using join buffer (Block Nested Loop) |+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+2 rows in set, 1 warning (0.03 sec)

          Not exists

          當我們使用左(外)連接時,如果WHERE子句中包含要求被驅(qū)動表的某個列等于NULL值的搜索條件,而且那個列又是不允許存儲NULL值的,那么在該表的執(zhí)行計劃的Extra列就會提示Not exists額外信息,比如這樣:

          mysql> EXPLAIN SELECT * FROM s1 LEFT JOIN s2 ON s1.key1 = s2.key1 WHERE s2.id IS NULL;+----+-------------+-------+------------+------+---------------+----------+---------+-------------------+------+----------+-------------------------+| id | select_type | table | partitions | type | possible_keys | key      | key_len | ref               | rows | filtered | Extra                   |+----+-------------+-------+------------+------+---------------+----------+---------+-------------------+------+----------+-------------------------+|  1 | SIMPLE      | s1    | NULL       | ALL  | NULL          | NULL     | NULL    | NULL              | 9688 |   100.00 | NULL                    ||  1 | SIMPLE      | s2    | NULL       | ref  | idx_key1      | idx_key1 | 303     | xiaohaizi.s1.key1 |    1 |    10.00 | Using where; Not exists |+----+-------------+-------+------------+-

          Using intersect(...)、Using union(...)和Using sort_union(...)

          如果使用了索引合并執(zhí)行查詢,則會出現(xiàn)Using intersect(...)或者Using union(...)或者Using sort_union(...)。比如:

          mysql> EXPLAIN SELECT * FROM s1 WHERE key1 = 'a' AND key3 = 'a';+----+-------------+-------+------------+-------------+-------------------+-------------------+---------+------+------+----------+-------------------------------------------------+| id | select_type | table | partitions | type        | possible_keys     | key               | key_len | ref  | rows | filtered | Extra                                           |+----+-------------+-------+------------+-------------+-------------------+-------------------+---------+------+------+----------+-------------------------------------------------+|  1 | SIMPLE      | s1    | NULL       | index_merge | idx_key1,idx_key3 | idx_key3,idx_key1 | 303,303 | NULL |    1 |   100.00 | Using intersect(idx_key3,idx_key1); Using where |+----+-------------+-------+------------+-------------+-------------------+-------------------+---------+------+------+----------+-------------------------------------------------+1 row in set, 1 warning (0.01 sec)

          Zero limit

          limit子句參數(shù)為0時,就會出現(xiàn)Zero limit

          Using filesort

          有一些情況下對結(jié)果集中的記錄進行排序是可以使用到索引的,比如下邊這個查詢:

          mysql> EXPLAIN SELECT * FROM s1 ORDER BY key1 LIMIT 10;+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-------+| id | select_type | table | partitions | type  | possible_keys | key      | key_len | ref  | rows | filtered | Extra |+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-------+|  1 | SIMPLE      | s1    | NULL       | index | NULL          | idx_key1 | 303     | NULL |   10 |   100.00 | NULL  |+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-------+1 row in set, 1 warning (0.03 sec)

          但是更多情況下,排序操作無法使用到索引,而是只能使用文件排序(filesort)。如果排序使用了filesort,那么在Extra列就會出現(xiàn)Using filesort

          Using temporary

          在許多查詢的執(zhí)行過程中,MySQL可能會借助臨時表來完成一些功能,比如去重、排序之類的,比如我們在執(zhí)行許多包含DISTINCTGROUP BYUNION等子句的查詢過程中,如果不能有效利用索引來完成查詢,MySQL很有可能尋求通過建立內(nèi)部的臨時表來執(zhí)行查詢。如果查詢中使用到了內(nèi)部的臨時表,在執(zhí)行計劃的Extra列將會顯示Using temporary提示,比方說這樣:

          mysql> EXPLAIN SELECT DISTINCT common_field FROM s1;+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-----------------+| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra           |+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-----------------+|  1 | SIMPLE      | s1    | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 9688 |   100.00 | Using temporary |+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-----------------+1 row in set, 1 warning (0.00 sec)

          執(zhí)行計劃中出現(xiàn)Using temporary并不是一個好的征兆,因為建立與維護臨時表要付出很大成本的,所以我們最好能使用索引來替代掉使用臨時表。

          Start temporary, End temporary

          查詢優(yōu)化器會優(yōu)先嘗試將IN子查詢轉(zhuǎn)換成semi-join,而semi-join又有好多種執(zhí)行策略,當執(zhí)行策略為DuplicateWeedout時,也就是通過建立臨時表來實現(xiàn)為外層查詢中的記錄進行去重操作時,驅(qū)動表查詢執(zhí)行計劃的Extra列將顯示Start temporary提示,被驅(qū)動表查詢執(zhí)行計劃的Extra列將顯示End temporary提示,就是這樣:

          mysql> EXPLAIN SELECT * FROM s1 WHERE key1 IN (SELECT key3 FROM s2 WHERE common_field = 'a');+----+-------------+-------+------------+------+---------------+----------+---------+-------------------+------+----------+------------------------------+| id | select_type | table | partitions | type | possible_keys | key      | key_len | ref               | rows | filtered | Extra                        |+----+-------------+-------+------------+------+---------------+----------+---------+-------------------+------+----------+------------------------------+|  1 | SIMPLE      | s2    | NULL       | ALL  | idx_key3      | NULL     | NULL    | NULL              | 9954 |    10.00 | Using where; Start temporary ||  1 | SIMPLE      | s1    | NULL       | ref  | idx_key1      | idx_key1 | 303     | xiaohaizi.s2.key3 |    1 |   100.00 | End temporary                |+----+-------------+-------+------------+------+---------------+----------+---------+-------------------+------+----------+------------------------------+2 rows in set, 1 warning (0.00 sec)

          LooseScan

          在將In子查詢轉(zhuǎn)為semi-join時,如果采用的是LooseScan執(zhí)行策略,則在驅(qū)動表執(zhí)行計劃的Extra列就是顯示LooseScan提示,比如這樣:

          mysql> EXPLAIN SELECT * FROM s1 WHERE key3 IN (SELECT key1 FROM s2 WHERE key1 > 'z');+----+-------------+-------+------------+-------+---------------+----------+---------+-------------------+------+----------+-------------------------------------+| id | select_type | table | partitions | type  | possible_keys | key      | key_len | ref               | rows | filtered | Extra                               |+----+-------------+-------+------------+-------+---------------+----------+---------+-------------------+------+----------+-------------------------------------+|  1 | SIMPLE      | s2    | NULL       | range | idx_key1      | idx_key1 | 303     | NULL              |  270 |   100.00 | Using where; Using index; LooseScan ||  1 | SIMPLE      | s1    | NULL       | ref   | idx_key3      | idx_key3 | 303     | xiaohaizi.s2.key1 |    1 |   100.00 | NULL                                |+----+-------------+-------+------------+-------+---------------+----------+---------+-------------------+------+----------+-------------------------------------+2 rows in set, 1 warning (0.01 sec)

          FirstMatch(tbl_name)

          在將In子查詢轉(zhuǎn)為semi-join時,如果采用的是FirstMatch執(zhí)行策略,則在被驅(qū)動表執(zhí)行計劃的Extra列就是顯示FirstMatch(tbl_name)提示,比如這樣:

          mysql> EXPLAIN SELECT * FROM s1 WHERE common_field IN (SELECT key1 FROM s2 where s1.key3 = s2.key3);+----+-------------+-------+------------+------+-------------------+----------+---------+-------------------+------+----------+-----------------------------+| id | select_type | table | partitions | type | possible_keys     | key      | key_len | ref               | rows | filtered | Extra                       |+----+-------------+-------+------------+------+-------------------+----------+---------+-------------------+------+----------+-----------------------------+|  1 | SIMPLE      | s1    | NULL       | ALL  | idx_key3          | NULL     | NULL    | NULL              | 9688 |   100.00 | Using where                 ||  1 | SIMPLE      | s2    | NULL       | ref  | idx_key1,idx_key3 | idx_key3 | 303     | xiaohaizi.s1.key3 |    1 |     4.87 | Using where; FirstMatch(s1) |+----+-------------+-------+------------+------+-------------------+----------+---------+-------------------+------+----------+-----------------------------+2 rows in set, 2 warnings (0.00 sec)


          推薦閱讀

          阿里精選:Java 代碼精簡之道

          Java8 中用法優(yōu)雅的 Stream,性能也""優(yōu)雅""嗎?

          瀏覽 31
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  一区二区三区最新 | 黄片大全在线播放 | 久久99无码精品亚洲日韩 | 一区二区三区四区久久 | 成人免费视屏 |