MySQL是目前業界最爲流行的關係型數據庫之一,而索引的優化也是數據庫性能優化的關鍵之一。因此,充分地瞭解MySQL索引有助於提高開發人員對MySQL數據庫的使用優化能力。
MySQL的索引有不少種類型,能夠爲不一樣的場景提供更好的性能。而B-Tree索引是最爲常見的MySQL索引類型,通常談論MySQL索引時,若是沒有特別說明,就是指B-Tree索引。本文就詳細講解一下B-Tree索引的的底層結構,使用原則和特性。
爲了節約你的時間,本文的主要內容以下:html
B-Tree索引使用B-Tree來存儲數據,固然不一樣存儲引擎的實現方式不一樣。B-Tree一般意味着全部的值都是按順序存儲的,而且每個葉子頁到根的距離相同,圖1展現了B-Tree索引的抽象表示,由此能夠看出MySQL的B-Tree索引的大體工做機制。mysql
B-Tree索引的底層數據結構通常是B+樹,其具體數據結構和優點這裏就不做詳細描述,圖1展現了B-樹索引的抽象表示,大體反應了MyISAM索引是如何工做的,而InnoDB使用的結構有所不一樣。算法
MySQL能夠在單獨一列上添加B-Tree索引,也能夠在多列數據上添加B-Tree索引,多列的數據按照添加索引聲明的順序組合起來,存儲在B-Tree的頁中。假設有以下數據表:sql
CREATE TABLE People ( last_name varchar(50) not null, first_name varchar(50) not null, birthday date not null, gender enum('m','f') not null key(last_name, first_name, birthday) );
對於表中的每一行數據,索引中包含了last_name,first_name和birthday列的值,圖2展現了該索引是如何組織數據的存儲的。數據庫
B-Tree索引使用B-Tree做爲其存儲數據的數據結構,其使用的查詢規則也由此決定。通常來講,B-Tree索引適用於全鍵值、鍵值範圍和鍵前綴查找,其中鍵前綴查找只適用於根據最左前綴查找。B-Tree索引支持的查詢原則以下所示:性能優化
由於索引樹的節點是有序的,因此除了按值查找以外,索引還能夠用於查詢中的ORDER BY操做(按順序查找),若是ORDER BY子句知足前面列出的幾種查詢類型,則這個索引也能夠知足對應的排序需求。微信
下面是一些關於B-Tree索引的限制:數據結構
聚簇索引並非一種單獨的索引類型,而是一種數據存儲方式。具體的細節依賴於其實現方式,可是InnoDB的聚簇索引實際上在同一個結構中保存了B-Tree索引和數據行。工具
當表有聚簇索引時,它的數據行實際上存放在索引的葉子頁中,這也就是說數據行和相鄰的鍵值緊湊地存儲在一塊兒。性能
圖3展現了聚簇索引中的記錄是如何存放的。注意到,葉子頁包含了行的所有數據行,可是節點頁只包含了索引列。
聚簇索引可能對性能有幫助,但也可能致使嚴重的性能問題。聚簇的數據是有一些重要的優勢:
若是在設計表和查詢時能充分利用上面的優勢,那麼就能極大地提高性能。同時,聚簇索引也有一些缺點:
聚簇索引和非聚簇索引的數據分佈有區別,以及對應的主鍵索引和二級索引的數據分佈也有區別,一般會讓人感到困惑和意外。圖4展現了MyISAM和InnoDB的不一樣索引和數據存儲方式。
MyISAM的數據分佈很是簡單,按照數據插入的順序存儲在磁盤上,主鍵索引和二級索引的葉節點存儲着指針,指向對應的數據行。
InnoDB中,聚簇索引「就是」表,因此不會像MyISAM那樣須要獨立的行存儲。聚簇索引的每一個葉節點都包含了主鍵值和全部的剩餘列(在此例中是col2)。
InnoDB的二級索引和聚簇索引很不一樣。InnoDB二級索引的葉節點中存儲的不是「行指針」,而是主鍵值,並以此做爲指向行的「指針」。
MySQL並不支持鬆散索引掃描,也就是沒法按照不連續的方式掃描一個索引。一般,MySQL的索引掃描須要先定義一個起點和終點,即便須要的數據只是這段索引中不多數的幾個,MySQL仍然須要掃描這段索引中的每一個條目。
下面,咱們經過一個示例說明這點,假設咱們有以下索引(a,b),有下面的查詢:
mysql>SELECT * FROM tb1 WHERE b BETWEEN 2 AND 3;
由於索引的前導字段是列a,可是在查詢中只指定了字段b,MySQL沒法使用這個索引,從而只能經過全表掃描找到匹配的行,如圖5所示。
瞭解索引的物理結構的話,不難發現還能夠有一個更快的辦法執行上面的查詢。索引的物理結構(不是存儲引擎的API)是的能夠先掃描a列第一個值對應的b列的範圍,而後再跳到a列第二個不不一樣值掃描對應的b列的範圍。圖6展現了若是由MySQL來實現這個過程會怎樣。
注意到,這時就無須再使用WHERE子句過濾,由於鬆散索引掃描已經跳過了全部不須要的記錄。
MySQL 5.0以後的版本,在某些特殊的場景下是可使用鬆散索引掃描的,例如,在一個分組查詢中須要找到分組的最大值和最小值:
mysql> EXPLAIN SELECT actor_id, MAX(film_id) -> FROM sakila.film.film_actor -> GROUP BY actor_id; ********************************************* 1. row *********************************** id: 1 select_type: SIMPLE table: film_actor type: range possible_keys: NULL key: PRIMARY key_len: 2 ref: NULL rows: 396 Extra: Using index for group-by
在EXPLAIN中的Extra字段顯示"Using index for group-by",表示這裏將使用鬆散索引掃描。
索引除了是一種查找數據的高效方式以外,也是一種列數據的直接獲取方式。MySQL可使用索引來直接獲取列的數據,這樣就不須要讀取數據行。若是一個索引包含全部須要查詢的字段的值,咱們就稱之爲「覆蓋索引」。
覆蓋索引是很是有用的工具,可以極大地提升性能。SQL查詢只須要掃描索引而無需回表,會帶來不少好處:
當發起一個被覆蓋索引的查詢(也叫索引覆蓋查詢)時,在EXPLAIN的Extra列能夠看到"Using Index"的信息。例如,表sakila.inventory有一個多列索引(store_id, film_id)。MySQL若是隻須要訪問這兩列,就可使用這個索引作覆蓋索引,以下所示:
mysql> EXPLAIN SELECT store_id, film_id FROM sakila.inventory *********************************1.row*************************************** id:1 select_type:SIMPLE table:inventory type:index possible_keys:NULL key:idx_store_id_film_id key_len:3 ref:NULL rows:4673 Extra:Using Index
訂閱最新文章,歡迎關注個人微信公衆號