1. 程式人生 > 資料庫 >MySQL(九)效能優化之索引原理與實際場景測試

MySQL(九)效能優化之索引原理與實際場景測試

MySQL效能優化之索引原理與實際場景測試

  • InnoDB儲存引擎只支援BTree索引
  • 顧名思義,B-tree索引使用B-tree的資料結構儲存資料,不同的儲存引擎以不同的方式使用B-Tree索引,比如MyISAM使用字首壓縮技術使得索引空間更小,而InnoDB則按照原資料格式儲存,且MyISAM索引在索引中記錄了對應資料的物理位置,而InnoDB則在索引中記錄了對應的主鍵數值。B-Tree通常意味著所有的值都是按順序儲存,並且每個葉子頁到根的距離相同。
  • B-Tree索引驅使儲存引擎不再通過全表掃描獲取資料,而是從索引的根節點開始查詢,在根節點和中間節點都存放了指向下層節點的指標,通過比較節點頁的值和要查詢值可以找到合適的指標進入下層子節點,直到最下層的葉子節點,最終的結果就是要麼找到對應的值,要麼找不到對應的值。整個B-tree樹的深度和表的大小直接相關。
  • • 全鍵值匹配:和索引中的所有列都進行匹配,比如查詢姓名為zhang san,出生於1982-1-1的人
  • • 匹配最左字首:和索引中的最左邊的列進行匹配,比如查詢所有姓為zhang的人
  • • 匹配列字首:匹配索引最左邊列的開頭部分,比如查詢所有以z開頭的姓名的人
  • • 匹配範圍值:匹配索引列的範圍區域值,比如查詢姓在li和wang之間的人
  • • 精確匹配左邊列並範圍匹配右邊的列:比如查詢所有姓為Zhang,且名字以K開頭的人
  • • 只訪問索引的查詢:查詢結果完全可以通過索引獲得,也叫做覆蓋索引,比如查詢所有姓為zhang的人的姓名

聚簇索引的葉子節點包含了行的全部資料,而節點頁只包含了索引列,比如下圖索引列 圖2

imgimg
MySQL索引種類:
BTREE:               B+樹索引
HASH:                HASH索引
FULLTEXT:            全文索引
RTREE:               R樹索引
B樹索引:

假如查詢id為33的資料,首先判斷大小後去"根"找P2對應的枝節點,然後在"枝節點"又找到P1,最後找到在"葉子節點"內id為33頁碼,然後根據"資料頁碼"直接去資料行找資料。

img

B+樹索引:

B+樹索引就是在查詢到id為33存在的"葉子節點"後,它可以通過圖中的粉紅色Q字母自動識別相鄰的葉子儲存的id號,下次再去查詢時可以跳過"根""枝節點"

直接去對應的葉子找到要找的id號。

B樹索引型別:
  • 聚集索引: 基於primary key自動生成的索引,效率極高,葉子節點儲存的是真實一行一行的資料行資料
  • 輔助(普通)索引: 基於普通列人為生成的索引,葉子節點儲存排序資料行後生成的資料頁頁碼號
輔助索引+聚集索引:

輔助索引生成的資料頁碼號值不再是單純的資料頁碼號,而是資料行的聚集索引生成的ID號對應輔助索引資料頁碼號

輔助索引與聚集索引工作機制參考連結:https://www.jianshu.com/p/3cd3cec2e28c
  • 也就是說生成的輔助索引葉子節點儲存的不是全部資料,還有指向聚集索引生成的書籤ID號
  • 注:聚集索引也一樣是生成ID號碼,分為根節點、枝節點、葉子節點,聚集索引的葉子節點儲存的是資料行的全部資料。

每個InnoDB的表都擁有一個索引,稱之為聚集索引,此索引中儲存著行記錄,一般來說,聚集索引是根據主鍵生成的。為了能夠獲得高效能的查詢、插入和其他資料庫操作,理解InnoDB聚集索引是很有必要的。

聚集索引:

聚集索引按照如下規則建立:
  • 1、當定義了主鍵後,InnoDB會利用主鍵來生成其聚集索引;
  • 2、如果沒有主鍵,InnoDB會選擇一個非空的唯一索引來建立聚集索引;
  • 3、如果這也沒有,InnoDB會隱式的建立一個自增的列來作為聚集索引。

聚集索引整體是一個b+樹非葉子節點存放的是鍵值葉子節點存放的是行資料,稱之為資料頁,這就決定了表中的資料也是聚集索引中的一部分,資料頁之間是通過一個雙向連結串列來連結的,上文說到B+樹是一棵平衡查詢樹,也就是聚集索引的資料儲存是有序的,但是這個是邏輯上的有序,但是在實際資料的物理儲存上是,因為資料頁之間是通過雙向連結串列來連線,假如物理儲存是順序的話,那維護聚集索引的成本非常的高。

輔助索引:

除了聚集索引之外的索引都可以稱之為輔助索引,與聚集索引的區別在於輔助索引的葉子節點中存放的是主鍵的鍵值。一張表可以存在多個輔助索引,但是隻能有一個聚集索引,通過輔助索引來查詢對應的航記錄的話,需要進行兩步,第一步通過輔助索引來確定對應的主鍵,第二步通過相應的主鍵值在聚集索引中查詢到對應的行記錄,也就是進行兩次B+樹搜尋。相反通過輔助索引來查詢主鍵的話,遍歷一次輔助索引就可以確定主鍵了,也就是所謂的索引覆蓋,不用回表(查詢聚集索引)。

  • **所謂遍歷:**是指沿著某條搜尋路線,依次對樹中每個結點均做一次且僅做一次訪問。
  • 第一步通過輔助索引來確定對應的主鍵,第二步通過相應的主鍵值在聚集索引中查詢到對應的行記錄

img

如何解決非聚集索引二次查詢的問題:
  • 建立兩列以上的索引,即可查詢聯合索引裡的列的資料而不需要進行回表二次查詢,如index(col1, col2),執行下面的語句:

    select col1, col2 from t1 where col1 = '213';
    

因為聯合索引的列包括了col1和col2,不需要查詢別的列,所以不需要進行二次查詢。

要注意使用聯合索引需要滿足最左側索引的原則,也就是查詢的時候如果where條件裡面沒有最左邊的一到多列,索引就不會起作用。

通俗的講講最左索引原則吧:
  • 假設建立了聯合索引index(A,B,C),那麼其實相當於建立瞭如下三個組合索引:

    index(A,B,C)
    index(A,B)
    index(A)
    
  • 這就是最左索引原則,就是從最左側開始組合。

建立索引的是三種途徑包括:
直接建立索引
  • column(length)是取欄位資料的前幾個字元做索引
CREATE INDEX index_name ON table(column(length))
修改表結構的方式新增索引
  • column(length)是取欄位資料的前幾個字元做索引
ALTER TABLE table_name ADD INDEX index_name ON (column(length))
建立表的時候同時建立索引
  • column(length)是取欄位資料的前幾個字元做索引
CREATE TABLE `table` (
`id` int(11) NOT NULL AUTO_INCREMENT ,
`title` char(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL ,
`content` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL ,
`time` int(10) NULL DEFAULT NULL ,
PRIMARY KEY (`id`),
INDEX index_name (title(length))
)
刪除索引
DROP INDEX index_name ON table

MySQL聚集索引:

**表示:**PRI

聚集索引原理:
  • 聚簇索引的葉子節點包含了行的全部資料,而節點頁只包含了索引列
  • 每個InnoDB表都會有一個特殊的索引,叫聚簇索引,索引中包含了所有的行資料。聚簇索引和主鍵是一個意思的兩種叫法
  • 當顯示定義一個主鍵時,則InnoDB就把它作為聚簇索引,當表中沒有代表唯一的一個或一組欄位時,可以增加一個auto-increment欄位作為主鍵。
  • 當沒有定義主鍵時,則MySQL會尋找是否有非NULL的唯一索引,如果有就把第一個唯一索引作為聚簇索引
  • 當沒有主鍵或合適的唯一索引時,InnoDB內部會建立一個虛構的聚簇索引,其中包含 row ID。
聚集索引的優勢:
  • 當SQL語句通過聚簇索引訪問表資料時,由於通過索引能直接定位並訪問表資料,所以效能很高。
  • 相關資料會儲存在一起,比如表是包含使用者的郵件資訊,通過使用者ID建立聚簇索引,則查詢一個使用者的所有郵件只需要讀取少量的資料頁。
  • 使用覆蓋索引掃描的查詢可以直接使用頁節點上的主鍵值
查看錶索引:
mysql> desc temp;
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+
| id    | int(11)     | NO   | PRI | NULL    | auto_increment |
| name  | varchar(64) | YES  |     | NULL    |                |
+-------+-------------+------+-----+---------+----------------+
2 rows in set (0.01 sec)
建立自增主鍵:
# 對id列新增自增
mysql> alter table temp modify id int(11) AUTO_INCREMENT;

或

# 對id列新增主鍵與自增
mysql> alter table temp modify id int(11) primary key AUTO_INCREMENT;
刪除主鍵:
# 先把自增刪除
mysql> alter table temp modify id int(11);

# 刪除主鍵
mysql> alter table temp drop primary key;

mysql> desc temp;
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id    | int(11)     | NO   |     | NULL    |       |
| name  | varchar(64) | YES  |     | NULL    |       |2019-11-06 15:36:06 星期三
+-------+-------------+------+-----+---------+-------+
2 rows in set (0.00 sec)

MySQL輔助索引:

**表示:**MUL

查看錶索引:
mysql> DESC student;
+----------+---------------------+------+-----+-------------------+----------------+
| Field    | Type                | Null | Key | Default           | Extra          |
+----------+---------------------+------+-----+-------------------+----------------+
| id       | int(11)             | NO   | PRI | NULL              | auto_increment |
| name     | varchar(50)         | NO   |     | NULL              |                |
| cardid   | char(18)            | NO   | UNI | NULL              |                |
| age      | tinyint(3) unsigned | NO   |     | 18                |                |
| gender   | enum('男','女')     | NO   |     | 男                |                |
| birthday | datetime            | NO   |     | CURRENT_TIMESTAMP |                |
| tnum     | char(11)            | NO   | UNI | NULL              |                |
| state    | enum('1','0')       | NO   |     | 1                 |                |
+----------+---------------------+------+-----+-------------------+----------------+

PRI是主鍵索引
UNI是唯一索引(不能有重複的值)
新增輔助索引:
#對name行新增索引並命名為idx_name,把name行對應的資料新增為索引
mysql> alter table student add index idx_name(name);
查詢輔助索引:
#從student表中查詢索引
mysql> show index from student;
刪除輔助索引:
#對name行刪除idx_name索引
mysql> alter table student drop index idx_name;

MySQL唯一索引:

表示: UNI

  • 資料值不唯一不能建立唯一索引
查看錶索引:
mysql> DESC student;
+----------+---------------------+------+-----+-------------------+----------------+
| Field    | Type                | Null | Key | Default           | Extra          |
+----------+---------------------+------+-----+-------------------+----------------+
| id       | int(11)             | NO   | PRI | NULL              | auto_increment |
| name     | varchar(50)         | NO   |     | NULL              |                |
| cardid   | char(18)            | NO   | UNI | NULL              |                |
| age      | tinyint(3) unsigned | NO   |     | 18                |                |
| gender   | enum('男','女')     | NO   |     | 男                |                |
| birthday | datetime            | NO   |     | CURRENT_TIMESTAMP |                |
| tnum     | char(11)            | NO   | UNI | NULL              |                |
| state    | enum('1','0')       | NO   |     | 1                 |                |
+----------+---------------------+------+-----+-------------------+----------------+
檢視資料有沒有重複值:
#判斷tnum列的資料重複次數大於0的
mysql> select tnum,count(tnum) from student group by tnum having count(tnum) > 0;
+-------------+----+
| tnum        | ct |
+-------------+----+
| 18805403432 |  1 |
| 18805407666 |  1 |
| 18805407677 |  1 |
| 18805407688 |  1 |
| 18805407699 |  1 |
| 81818181818 |  1 |
+-------------+----+
建立唯一索引:
#基於tnum列的資料建立唯一索引,並命名為idx_tnum
mysql> alter table student add unique index idx_tnum(tnum);
查詢唯一索引:
#從student表中查詢索引
mysql> show index from student;
刪除唯一索引:
#刪除基於tnum列建立的唯一索引
mysql> alter table student drop index idx_tnum;

MySQL字首索引:

  • 當索引的字元太過於長時,需要把查詢字串的列做字首索引
建立一個200字元的行:
mysql> alter table student add note varchar(200);

mysql> desc student;
+----------+---------------------+------+-----+-------------------+----------------+
| Field    | Type                | Null | Key | Default           | Extra          |
+----------+---------------------+------+-----+-------------------+----------------+
| id       | int(11)             | NO   | PRI | NULL              | auto_increment |
| name     | varchar(50)         | NO   |     | NULL              |                |
| cardid   | char(18)            | NO   | UNI | NULL              |                |
| age      | tinyint(3) unsigned | NO   |     | 18                |                |
| gender   | enum('男','女')     | NO   |     | 男                |                |
| birthday | datetime            | NO   |     | CURRENT_TIMESTAMP |                |
| tnum     | char(11)            | NO   | UNI | NULL              |                |
| state    | enum('1','0')       | NO   |     | 1                 |                |
| note     | varchar(200)        | YES  |     | NULL              |                |
+----------+---------------------+------+-----+-------------------+----------------+
建立字首索引:
#基於note行建立字首索引,也就是每一條資料的前10個字元作為索引
alter table student add index idx_note(note(10));
查詢字首索引:
#從student表中查詢索引
mysql> show index from student;
刪除字首索引:
#刪除基於note行建立的字首索引
alter table student drop index idx_note;

MySQL聯合索引:

  • 當用戶有多個條件時需要做聯合索引
  • 比如男人找女朋友,要求是性別:女,年齡:25,身材:火辣,這就是三個資料列,也就是把這三個列共同建立一個索引。
#根據年齡與性別建立索引
mysql> desc student;
+----------+---------------------+------+-----+-------------------+----------------+
| Field    | Type                | Null | Key | Default           | Extra          |
+----------+---------------------+------+-----+-------------------+----------------+
| id       | int(11)             | NO   | PRI | NULL              | auto_increment |
| name     | varchar(50)         | NO   |     | NULL              |                |
| cardid   | char(18)            | NO   | UNI | NULL              |                |
| age      | tinyint(3) unsigned | NO   |     | 18                |                |
| gender   | enum('男','女')     | NO   |     | 男                |                |
| birthday | datetime            | NO   |     | CURRENT_TIMESTAMP |                |
| tnum     | char(11)            | NO   | UNI | NULL              |                |
| state    | enum('1','0')       | NO   |     | 1                 |                |
| note     | varchar(200)        | YES  |     | NULL              |                |
+----------+---------------------+------+-----+-------------------+----------------+
建立聯合索引:
#建立一個性別與年齡的聯合索引
alter table student add index idx_object(gender,age);
查詢聯合索引:
#從student表中查詢索引
mysql> show index from student;
刪除聯合索引:
#刪除基於年齡與性別的聯合索引
alter table student drop index idx_object;

MySQL覆蓋索引:

  • 當select語句 where多個條件時,為了避免輔助索引的葉子節點資料頁資料不夠導致要查詢主鍵索引,所以要建立覆蓋索引。
  • 也就是說另類的聯合索引
  • 假設應用程式經常執行:select name,age,gender from students where name='olda';
  • 如果在name欄位建立輔助索引,那在葉子節點資料頁中只包含資料 olda 與 id 主鍵值。不滿足 name,age,gender 三個欄位要求的資料,這時就會通過主鍵 id 值去二次查詢聚集索引
    img
  • 如果在 name,age,gender三個欄位建立聯合覆蓋式索引,讓葉子節點資料頁中包含資料 id,name,age,gender 四個欄位的值,那就滿足了當前select語句的需求了,不需要二次查詢聚集索引
    img
mysql> desc students;
+----------+---------------------+------+-----+-------------------+-------------------+
| Field    | Type                | Null | Key | Default           | Extra             |
+----------+---------------------+------+-----+-------------------+-------------------+
| id       | int(11)             | NO   | PRI | NULL              | auto_increment    |
| name     | varchar(50)         | NO   |     | NULL              |                   |
| age      | tinyint(3) unsigned | NO   |     | 18                |                   |
| gender   | enum('男','女')     | NO   |     | 男                |                   |
| birthday | datetime            | NO   |     | CURRENT_TIMESTAMP | DEFAULT_GENERATED |
+----------+---------------------+------+-----+-------------------+-------------------+
5 rows in set (0.01 sec)

# 當有經常使用的欄位資料查詢時,可以考慮建立覆蓋索引
mysql> select name,age,gender from students where name='olda';

# 根據 select 的欄位進行建立索引
mysql> alter table students add index idx_name(name,age,gender);

MySQL索引執行計劃:

建立索引的原則:

當資料表過大時,可以選擇建立輔助索引

當查詢的資料表內值唯一,可以選擇建立唯一索引

當資料表值字元過長,可以選擇建立字首索引

當資料表查詢的條件不唯一時,可以選擇建立聯合索引

select語句查詢方式:

優化器決定走全表掃描還是索引掃描

怎麼確認語句是否走索引掃描?

1、從查詢速度上
2、優化器執行計劃選擇情況

優化原則:

優化原則一:
  • 儘可能消除全表掃描,除非表資料量是在萬條以下。
優化原則二:
  • 增加適當的索引能提高查詢的速度,但增加索引需要遵循一定的基本規則。
  1. 加在where條件上
  2. 加在表之間join的鍵值上
  3. 如果查詢範圍是少量欄位,可以考慮增加覆蓋索引(僅走索引)
  4. 有多個查詢條件時,考慮增加複合索引,並把最常使用的欄位放在索引前面
  5. 不要將索引加在區別率不高的欄位上
  6. 欄位上增加函式,則欄位上的索引用不了,需考慮改變寫法
建索引原則:
(1) 必須要有主鍵,如果沒有可以做為主鍵條件的列,建立無關列

(2) 經常做為where條件列  order by  group by  join on, distinct 的條件(業務:產品功能+使用者行為)

(3) 最好使用唯一值多的列作為索引,如果索引列重複值較多,可以考慮使用聯合索引

(4) 列值長度較長的索引列,我們建議使用字首索引.

(5) 降低索引條目,一方面不要建立沒用索引,不常使用的索引清理,percona toolkit(xxxxx)

(6) 索引維護要避開業務繁忙期
強制使用指定索引:
  • force index(idx_name) 強制使用索引
mysql> select sname from students force index(idx_name);
+--------+
| sname  |
+--------+
| Andrew |
| Andy   |
| Bob    |
| Cindy  |
| John   |
| Mike   |
| Ruth   |
| Susan  |
+--------+
8 rows in set (0.01 sec)          # 強制使用指定索引後使用了10毫秒

mysql> select sname from students;
+--------+
| sname  |
+--------+
| Andrew |
| Andy   |
| Bob    |
| Cindy  |
| John   |
| Mike   |
| Ruth   |
| Susan  |
+--------+
8 rows in set (0.00 sec)          # 沒有強制使用指定索引 使用了0毫秒

索引執行計劃解釋:

  • 執行計劃用來顯示對應語句在MySQL中是如何執行的。explain語句對select,delete,update,insert,replace語句有效。
  • id列:表示執行順序,值越大則優先順序越高;值相同則從上而下執行

img

select_type列:
simple:                表示不需要union操作或者不包含子查詢的簡單select查詢。有連線查詢時,外層的查詢為simple,且 只有一個

primary:               一個需要union操作或者含有子查詢的select,位於最外層的單位查詢的select_type即為primary。 且只有一個

union:                 union連線的兩個select查詢,第一個查詢是dervied派生表,除了第一個表外,第二個以後的表 select_type都是union

dependent union:       與union一樣,出現在union 或union all語句中,但是這個查詢要受到外部查詢的影響

union result:          包含union的結果集,在union和union all語句中,因為它不需要參與查詢,所以id欄位為null

subquery:              除了from字句中包含的子查詢外,其他地方出現的子查詢都可能是subquery

dependent subquery:    與dependent union類似,表示這個subquery的查詢要受到外部表查詢的影響

derived:               from字句中出現的子查詢,也叫做派生表,其他資料庫中可能叫做內聯檢視或巢狀select
table列:
  • 顯示的查詢表名,如果查詢使用了別名,那麼這裡顯示的是別名,如果不涉及對資料表的操作,那麼這顯示為null,如果顯示為尖括號括起來的就表示這個是臨時表,後邊的N就是執行計劃 中的id,表示結果來自於這個查詢產生。如果是尖括號括起來的<union M,N>,與類似, 也是一個臨時表,表示這個結果來自於union查詢的id為M,N的結果集。
Type列:
  • 表示訪問型別,效能從低到高依次是:ALL -> index -> range -> ref -> eq_ref -> const,system -> NULL
ALL:              Full Table Scan, MySQL將遍歷全表以找到匹配的行

index:            Full Index Scan,index與ALL區別為index型別只遍歷索引樹

range:            索引範圍掃描,對索引的掃描開始於某一點,返回匹配值域的行,常見於between、<、>等的查詢

unique_subquery:  用於where中的in形式子查詢,子查詢返回不重複值唯一值

index_subquery:   用於in形式子查詢使用到了輔助索引或者in常數列表,子查詢可能返回重複值,可以使用索引將子查詢去重

ref:              非唯一性索引掃描,返回匹配某個單獨值的所有行。常見於使用非唯一索引和唯一索引的非唯一字首進行的查詢

eq_ref:           唯一性索引掃描,對於每個索引鍵,表中只有一條記錄與之匹配。常見於主鍵或唯一索引掃描的多表連結操作中

system:           當MySQL對查詢某部分進行優化,並轉換為一個常量時,使用這些型別訪問。如將主鍵置於where列表中,MySQL就能將該 查詢轉換為一個常量。System為表中只有一行資料或者是空表,且只能用於myisam和memory表。如果是Innodb引擎表,type列在這個情況通常都是all或者index

const:             使用唯一索引或者主鍵,返回記錄一定是1行記錄的等值where條件時,通常type是const。其他資料庫也叫做唯一索引掃描

NULL:             MySQL在優化過程中分解語句,執行時甚至不用訪問表或索引
possible_keys列:
  • 表示MySQL能使用哪個索引在表中找到行,查詢涉及到的欄位上若存在索引,則該索引將被列出,但不一定被查詢使用
Key列:
  • 表示MySQL在查詢中實際使用的索引,若沒有使用索引,顯示為NULL
key_len列:
  • 表示索引中使用的位元組數,可通過該列計算查詢中使用的索引的長度
Ref列:
  • 如果是使用的常數等值查詢,這裡會顯示const,如果是連線查詢,被驅動表的執行計劃這裡會顯示驅動表的關聯欄位,如果是條件使用了表示式或者函式,或者條件列發生了內部隱式轉換,這裡可能顯示為func
Rows列:
  • 表示MySQL根據表統計資訊及索引選用情況,估算的找到所需的記錄所需要讀取的行數
Extra列:
Using index:       該值表示相應的select操作中使用了覆蓋索引(Covering Index)

Using where:       表示MySQL伺服器在儲存引擎收到記錄後進行“後過濾”   多個where條件,根據查詢出的資料進行二次篩選

Using temporary:   表示MySQL需要使用臨時表來儲存結果集,常見於排序和分組查詢

Using filesort:    MySQL中無法利用索引完成的排序操作稱為“檔案排序”,常見於order by和group by語句中

explain(desc)使用場景(面試題)

題目意思: 我們公司業務慢,請你從資料庫的角度分析原因

一.mysql出現效能問題,我總結有兩種情況:

1)應急性的慢:突然夯住 
應急情況:資料庫hang(卡了,資源耗盡)
處理過程:
1. show processlist;   獲取到導致資料庫hang的語句
2. explain 分析SQL的執行計劃,有沒有走索引,索引的型別情況
3. 建索引,改語句
2)一段時間慢(持續性的):
處理過程:
1.記錄慢日誌slowlog,分析slowlog
2.explain 分析SQL的執行計劃,有沒有走索引,索引的型別情況 
3.建索引,改語句

檢視索引資料區別度:

  • 區別度越高越好,說明不重複的資料越多

Cardinality: 3 欄位是資料不重複的資料行數

mysql> show index from temp;
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| temp  |          1 | idx_name |            1 | name        | A         |           3 |     NULL |   NULL | YES  | BTREE      |         |               | YES     | NULL       |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
1 row in set (0.01 sec)

優化器執行計劃引數詳解:

table:              被掃描的表名
type:               優化器走的索引型別
    ALL              全表掃描
        示例:desc select * from city;

    index            需要掃描整個索引樹,獲取到想要資料,比ALL效能好,順序IO,可以減少回表查詢
        示例:desc select id from city;

    range            基於索引範圍作為查詢條件,> < >= <= or like
        示例:desc select * from city where id<10;

    ref              普通索引,等值查詢
        示例:desc select * from city where CountryCode='CHN';
        示例:desc select * from city where countrycode='CHN' union all select * from city where countrycode='USA';

    eq_ref           在發生join操作時,on的條件列是主鍵或唯一鍵
        示例:desc select country.name,city.name,country.SurfaceArea from city join country on city.countrycode=country.code where city.name='shenyang';

    const或system     唯一鍵索引,主鍵索引等值查詢
        示例:desc select * from city where id='1';

    NULL              索引中掃描不到這個資料
        示例:desc select * from city where id=5000;
possible_keys         可能會用到的索引

key                   真正使用的索引

key_len               索引佔用磁碟空間大小,越小越好

rows                  掃描後返回的資料行,超過所有資料行的25%會自動轉為全表掃描

Extra                 Using index
命令檢視優化器選擇後的執行計劃:
desc + select語句` 或 `explain + select語句
通過全表查詢:
#看type的型別與key最後走的索引,(這是一個全表查詢的選擇)

mysql> explain select * from student;
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------+
| id | select_type | table   | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------+
|  1 | SIMPLE      | student | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    6 |   100.00 | NULL  |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------+
1 row in set, 1 warning (0.10 sec)
通過idx_name查詢:
#這是一個通過自定義的idx_name輔助索引查詢的

mysql> explain select * from student where name='olda';
+----+-------------+---------+------------+------+---------------+----------+---------+-------+------+----------+-------+
| id | select_type | table   | partitions | type | possible_keys | key      | key_len | ref   | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+----------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | student | NULL       | ref  | idx_name      | idx_name | 152     | const |    1 |   100.00 | NULL  |
+----+-------------+---------+------------+------+---------------+----------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.01 sec)
explain(desc):

執行過程Extra欄位的引數意思

  • using index:表示資料不需要回表查詢,查詢所需的資料都是從索引檔案(資料)中獲取,
  • using where:需要回表進行資料查詢