MySQL 索引及其分類

概述

用過 mysql 的童鞋都知道創建索引的必要性,但是很多人對創建索引的目的僅僅停留於創建索引可讓查詢變快 mysql

然而,爲了達到這個目的,科學合理的創建索引也是很是有必要的 sql

索引可以輕易將查詢性能提升幾個數量級,而一個「最優」索引有時比一個「好的」索引性能要高兩個數量級 數據庫

 

在 MySQL 中,索引能夠包含一個活多個列的值,由於 MySQL 只能高效地使用索引的最左前綴列,因此包含多個列的索引中列的順序也十分重要 數據結構

而建立一個包含兩個列的索引和建立兩個分別包含一列的索引是大不相同的 函數

 

索引的類型

MySQL 中,索引的類型有不少類型,可以爲不一樣場景提供更好的性能 性能

索引是在存儲引擎層實現的,不一樣存儲引擎的索引工做方式不一樣,也並非全部引擎都支持所有的索引類型,而對於同一類索引,不一樣引擎的底層實現也多是不一樣的 優化

 

B-Tree 索引

大多數 MySQL 存儲引擎都支持 B-Tree 索引,也所以,B-Tree 索引是最經常使用的索引類型,若是未加說明,索引通常都指的是 B-Tree 索引 搜索引擎

然而,雖然在建立表時關鍵字都是 B-Tree,可是各個存儲引擎的底層實現多是不一樣的,如 NDB 集羣存儲引擎內部實際上使用了 T-Tree 結構,而 innoDB 使用的 B+ Tree url

 

磁盤 IO 與預讀

因爲磁盤讀取靠的是機械運動,每次都要花費尋道時間、旋轉延遲、傳輸時間三部分時間才能讀取數據,總計時間是很是長的,若是針對數據庫的動輒十萬百萬乃至千萬級的數據查詢,每次幾毫秒的時間,結果將會是災難性的 spa

所以操做系統對此進行了一些優化,每次讀取時並不只僅讀取須要的數據,而是把相鄰數據所有讀取到內存緩衝區中,這樣,每次都讀取一頁數據(4KB 或 8KB),而針對一頁上數據的讀取,事實上僅進行了一次磁盤 IO 操做

 

B-Tree 的特性

B-Tree 的結構以下圖:

 

因爲 B 樹的多分支結構特性,致使樹的高度能夠大幅降低,這樣,若是每一個節點都存儲一頁數據,若是須要訪問第三層數據,則只須要進行三次磁盤 IO,這顯然大幅的節省了時間

B+ 樹與 B 樹的區別在於只有葉子節點存儲真實數據,其他非葉子結點僅做爲指引搜索方向的數據項

這樣存儲引擎再也不須要全表掃描,而是根據每一個節點的指引能夠快速找到須要的數據

 

同時,因爲 B 樹的結構特性,也致使全部的值一般都是按順序存儲的,所以在使用 ORDER BY 操做時,這個索引也能夠知足對應的排序需求

 

多列索引的匹配規則

 CREATE TABLE People (
    a    varchar(50)    not null,
    b    varchar(50)    not null,
    c    date        not null,
    d    date        not null,
    e    enum('m', 'f')    not null,
    key(a, b, c, d)
);

 

對於上面這個表,建立了四列索引,他們遵循下列規則

  • 最左前綴匹配原則

這是一個很是重要的原則,MySQL 會一直向右匹配直到遇到範圍查詢(>、<、between、like)

好比查詢 a="" and b="2" and c >= 3 and d = 4

在這個查詢中,d 是用不到索引的,而若是創建 (a, b, d, c) 則是能夠的

同時 where 語句中查詢的順序是能夠任意調整的,即 a、b、c、d 的順序能夠任意調整,MySQL 老是按照索引創建的順序進行查詢

 

  • 最大區分度原則

儘可能選擇區分度高的列做爲索引,或是將其放置在左端,區分度越高,即選出的結果行越少,則實際查詢的次數就會越少

 

  • 索引列不能參與計算

對於 from_unixtime(a) = '2014-05-29' 這樣的查詢是不能應用索引的,而應該優化成 a = from_unixtime('2014-05-29')

好比 a+1>5 只有優化爲 a > 4 纔會應用索引

 

  • 必須以最左列開始查詢

若是查詢 b = 5 and c < 2014 則不會應用索引,這也正是最左前綴匹配原則

 

  • 不能跳過索引中的列

對於查詢 a=5 and c > 2015,因爲跳過了 b 列,因此 c 不會應用索引

 

  • 說明

上述限制存在於 MySQL 5.5 及之前的數據庫版本中,將來的版本可能會取消某些限制

然而,能夠看到,建立表時怎樣選取索引的列,以及他們的排列順序是很是重要的

 

哈希索引

簡介

 CREATE TABLE testhash (
    a varchar(50) not null,
    b varchar(50) not null,
    KEY USING HASH(a)
) ENGINE=MEMORY;

 

上面建立表的過程當中建立了一個哈希索引

 

顧名思義,哈希索引的底層數據結構是用哈希表實現的,只有精確匹配索引全部列的查詢纔有效

索引會爲每一行數據創建一個很小的哈希碼,所以哈希索引佔用空間小,執行效率高,但只支持等值查詢,而不支持範圍查詢

同時,因爲哈希表並不按照值的大小順序存儲,所以在 ORDER BY 操做中並不會應用該索引,也不支持僅使用索引中部分列進行查找

可是,若是是某些特定適合使用哈希索引的場合,索引所帶來的性能提高將很是顯著,如經典的「星型」 schema,須要關聯不少查找表,哈希索引就很是適合查找表的需求

 

哈希索引與存儲引擎

哈希索引是 MEMORY 存儲引擎的默認索引方式,MEMORY 引擎同時也支持 B-Tree 索引,目前,在 MySQL 中,只有 MEMORY 引擎顯式支持哈希索引

 

InnoDB 引擎有一個特殊的功能 -- 自適應哈希索引,對於被頻繁使用的索引值,InnoDB 引擎會自動在內存中建立一個哈希索引,用戶只能經過配置選擇是否啓用這一特性,一旦啓用,該過程將是徹底自動,用戶沒法察覺的

InnoDB 建立的自適應哈希索引和真正的哈希索引並非一回事,而是在原有的 B-Tree 索引的基礎上,將檢索的值變成哈希碼,以下降磁盤使用

 

自定義哈希索引

針對不支持哈希索引的存儲引擎,用戶也能夠採用相似 InnoDB 的思路去自定義哈希索引

典型的如將 url 變成 CRC32,能夠有效節省磁盤使用,而且提升查詢速度

 

如針對下面的查詢:

 SELECT id FROM url WHERE url = 'http://www.techlog.cn/article/list/10182793';

這樣的查詢顯然是很耗時的,且若是爲 url 建立索引,索引也將很是龐大

 

優化成如下這樣:

 SELECT id FROM url WHERE crc32_url = CRC32('http://www.techlog.cn/article/list/10182793');

這樣,咱們爲 crc32_url 字段建立索引,索引的大小、查詢效率都會有顯著的提高

 

可是,這樣又須要維護一個新的字段 crc32_url,經過建立觸發器,能夠自動的添加該字段:

 CREATE TABLE pseudohash (
    id    int unsigned NOT NULL auto_increment,
    url    varchar(255) NOT NULL,
    url_crc    int unsigned NOT NULL DEFAULT 0,
    PRIMARY KEY(id)
    KEY(url_crc);
);

DELIMITER //

CREATE TRIGGER pseudohash_crc_ins BEFORE INSERT ON pseudohash FOR EACH ROW BEGIN
SET NEW.url_crc = crc32(NEW.url);
END;
//

CREATE TRIGGER pseudohash_crc_upd BEFORE UPDATE ON pseudohash FOR EACH ROW BEGIN
SET NEW.url_crc = crc32(NEW.url);
END;
//

DELIMITER ;

這樣,每當添加或修改 url 字段,觸發器會自動更新 url_crc 字段

 

因爲可能存在的哈希衝突,因此直接查詢可能會出現多條記錄,能夠優化爲:

 SELECT id FROM url WHERE crc32_url = CRC32('http://www.techlog.cn/article/list/10182793') and url = 'http://www.techlog.cn/article/list/10182793';

 

空間數據索引(R-Tree)

MyISAM 表支持空間索引,能夠用做地理數據存儲

與 B-Tree 索引不一樣,空間數據索引無需前綴查詢,他會從全部維度索引數據,能夠任意組合查詢

可是必須使用 MySQL 的 GIS 相關函數,如 MBRCONTAINS() 來維護數據,然而 MySQL 對 GIS 支持並不完善,因此大部分人不會使用這個特性

PostgreSQL 的 PostGIS 對 GIS 支持很好

 

全文索引

全文索引查找的是文本中的關鍵詞,而不是比較索引中的值,相似於搜索引擎

使用 MATCH AGAINST 操做進行索引,目前不支持中文

 

其餘索引

還有不少第三方存儲引擎使用其餘不一樣類型的數據結構來存儲索引,他們各自有不一樣的適用場景和優點

相關文章
相關標籤/搜索