1. 程式人生 > > MySql索引原理及使用

MySql索引原理及使用

索引用於快速查找出在某個列中有一特定值的行,不適用索引,MySQL必須從第一條記錄開始讀完整個表,直到找到相關行。表越大,查詢資料花費的時間越多。如果表中查詢的列有一個索引,MySQL能快速達到某個位置去搜索檔案,而不必檢視所有資料。因此使用索引是資料庫優化最直接有效的方式,所以瞭解索引對於開發人員尤為重要。

索引簡介

索引是一個單獨的、儲存在磁碟上的資料庫結構,他們包含著對資料表裡所有記錄的引用指標。使用索引用於快速找出在某個或者多個列中有一特定值的行,所有MySQL列型別都可以被索引,對於相關列使用索引是提高查詢操作速度的最佳途徑。

索引的優點:

1.通過建立唯一索引,可以保證資料庫表中每一行資料的唯一性。

2.可以大大加快資料的查詢速度,這也是建立索引的最主要原因。

3.在實現資料的參考完整性方面,可以加速表與表之間的連線。

4.在使用分組和排序字句進行資料查詢時,也可以顯著減少查詢中分組和排序的時間。

索引的缺點:

1.建立和維護索引要耗費時間,資料量越大耗費時間也會越長。

2.索引需要佔磁碟空間,除了資料表佔資料空間外,每一個索引還要佔一定的物理空間,如果有大量的索引,索引檔案可能比資料檔案更快達到最大檔案尺寸。

3.當對錶中的資料進行增加、刪除和修改的時候,索引也要動態的維護,這樣就降低了資料的維護速度。

索引的原理

一 索引原理
索引的目的在於提高查詢效率,與我們查閱圖書所用的目錄是一個道理:先定位到章,然後定位到該章下的一個小節,然後找到頁數。相似的例子還有:查字典,查火車車次,飛機航班等

本質都是:通過不斷地縮小想要獲取資料的範圍來篩選出最終想要的結果,同時把隨機的事件變成順序的事件,也就是說,有了這種索引機制,我們可以總是用同一種查詢方式來鎖定資料。

資料庫也是一樣,但顯然要複雜的多,因為不僅面臨著等值查詢,還有範圍查詢(>、<、between、in)、模糊查詢(like)、並集查詢(or)等等。資料庫應該選擇怎麼樣的方式來應對所有的問題呢?我們回想字典的例子,能不能把資料分成段,然後分段查詢呢?最簡單的如果1000條資料,1到100分成第一段,101到200分成第二段,201到300分成第三段......這樣查第250條資料,只要找第三段就可以了,一下子去除了90%的無效資料。但如果是1千萬的記錄呢,分成幾段比較好?稍有演算法基礎的同學會想到搜尋樹,其平均複雜度是lgN,具有不錯的查詢效能。但這裡我們忽略了一個關鍵的問題,複雜度模型是基於每次相同的操作成本來考慮的。而資料庫實現比較複雜,一方面資料是儲存在磁碟上的,另外一方面為了提高效能,每次又可以把部分資料讀入記憶體來計算,因為我們知道訪問磁碟的成本大概是訪問記憶體的十萬倍左右,所以簡單的搜尋樹難以滿足複雜的應用場景。

 二 磁碟IO與預讀

考慮到磁碟IO是非常高昂的操作,計算機作業系統做了一些優化,當一次IO時,不光把當前磁碟地址的資料,而是把相鄰的資料也都讀取到記憶體緩衝區內,因為區域性預讀性原理告訴我們,當計算機訪問一個地址的資料的時候,與其相鄰的資料也會很快被訪問到。每一次IO讀取的資料我們稱之為一頁(page)。具體一頁有多大資料跟作業系統有關,一般為4k或8k,也就是我們讀取一頁內的資料時候,實際上才發生了一次IO,這個理論對於索引的資料結構設計非常有幫助。

三、索引的資料結構
任何一種資料結構都不是憑空產生的,一定會有它的背景和使用場景,我們現在總結一下,我們需要這種資料結構能夠做些什麼,其實很簡單,那就是:每次查詢資料時把磁碟IO次數控制在一個很小的數量級,最好是常數數量級。那麼我們就想到如果一個高度可控的多路搜尋樹是否能滿足需求呢?就這樣,b+樹應運而生。

如上圖,是一顆b+樹,關於b+樹的定義可以參見B+樹,這裡只說一些重點,淺藍色的塊我們稱之為一個磁碟塊,可以看到每個磁碟塊包含幾個資料項(深藍色所示)和指標(黃色所示),如磁碟塊1包含資料項17和35,包含指標P1、P2、P3,P1表示小於17的磁碟塊,P2表示在17和35之間的磁碟塊,P3表示大於35的磁碟塊。真實的資料存在於葉子節點即3、5、9、10、13、15、28、29、36、60、75、79、90、99。非葉子節點只不儲存真實的資料,只儲存指引搜尋方向的資料項,如17、35並不真實存在於資料表中。

###b+樹的查詢過程

如圖所示,如果要查詢資料項29,那麼首先會把磁碟塊1由磁碟載入到記憶體,此時發生一次IO,在記憶體中用二分查詢確定29在17和35之間,鎖定磁碟塊1的P2指標,記憶體時間因為非常短(相比磁碟的IO)可以忽略不計,通過磁碟塊1的P2指標的磁碟地址把磁碟塊3由磁碟載入到記憶體,發生第二次IO,29在26和30之間,鎖定磁碟塊3的P2指標,通過指標載入磁碟塊8到記憶體,發生第三次IO,同時記憶體中做二分查詢找到29,結束查詢,總計三次IO。真實的情況是,3層的b+樹可以表示上百萬的資料,如果上百萬的資料查詢只需要三次IO,效能提高將是巨大的,如果沒有索引,每個資料項都要發生一次IO,那麼總共需要百萬次的IO,顯然成本非常非常高。

###b+樹性質
1.索引欄位要儘量的小:通過上面的分析,我們知道IO次數取決於b+數的高度h,假設當前資料表的資料為N,每個磁碟塊的資料項的數量是m,則有h=㏒(m+1)N,當資料量N一定的情況下,m越大,h越小;而m = 磁碟塊的大小 / 資料項的大小,磁碟塊的大小也就是一個數據頁的大小,是固定的,如果資料項佔的空間越小,資料項的數量越多,樹的高度越低。這就是為什麼每個資料項,即索引欄位要儘量的小,比如int佔4位元組,要比bigint8位元組少一半。這也是為什麼b+樹要求把真實的資料放到葉子節點而不是內層節點,一旦放到內層節點,磁碟塊的資料項會大幅度下降,導致樹增高。當資料項等於1時將會退化成線性表。

索引的分類

索引是在儲存引擎中實現的,因此,每種儲存引擎的索引都不一定完全相同,並且每種儲存引擎也不一定支援所有索引型別。根據儲存引擎定義每個表的最大索引數和最大索引長度。所有的儲存引擎支援每個表至少16個索引,總長度至少為256位元組,大多數儲存引擎有更高的限制。MySQL中索引的儲存型別有兩種:Btree和hash,具體和表的儲存引擎相關;MyISAM和InnoDB儲存引擎只支援Btree索引,MEMORY/HEAP儲存引擎可以支援hash和Btree索引。

MySQL索引的分類:

1.普通索引:MySQL的基本索引型別,允許索引欄位的列插入重複值和空值。

2.唯一索引:索引列欄位必須唯一,但允許有空值,如果是組合索引,則組合必須唯一,主鍵索引是一種特殊的唯一索引,不允許有空值。

3.單列索引和組合索引:單列索引只包含單個列,一個表可以有多個單列索引。組合索引指在表的多個欄位組合上建立索引,使用組合索引時遵循最左字首集合。

4.全文索引:全文索引型別為FullText,在定義索引的列上支援值得全文查詢,允許在這些索引列中插入重複值和空置,全文索引可以在char、varchar和text型別的列上建立,MySQL中只有MyISAM儲存引擎支援全文索引。

5.空間索引:空間索引是對空間資料型別的欄位建立的索引,只能在MyISAM儲存引擎的表中建立。

索引建立

建立表的時候建立索引

語法結構:

CREATE TABLE table_name [col_name data_type]

[UNIQUE | FULLTEXT | SPATITAL] [INDEX | KEY] [index_name] (col_name [length]) [ASC | DESC]

1.建立普通索引

最基本的索引型別,沒有唯一性之類的限制,其作用只是加快對資料庫的訪問速度。

CREATE TABLE book
(
book_id                        INT NOT NULL,
book_name                      VARCHAR(255) NOT NULL,
info                           VARCHAR(255) NOT NULL,
INDEX(info)
);

沒有指定索引名稱時會預設使用索引欄位作為索引名稱,此處為給欄位info建立名稱為info索引。

2.建立唯一索引

建立唯一索引的主要原因是減少查詢索引列操作的執行時間,尤其是對比較龐大的資料表,它與前面的普通索引類似,不同的就是:索引列的值必須唯一,但允許有空值。如果是組合索引,則列值的組合必須唯一。

CREATE TABLE t1
(
id                        INT NOT NULL,
name                      CHAR(30) NOT NULL,
UNIQUE INDEX UniqIdx(id)
); 

此處為給id欄位建立一個名為UniqIdx的唯一索引。

3.建立單列索引

單列索引是在資料表中某一個欄位上建立的索引,一個表中可以建立多個單列索引,前面兩個例子建立的都為單列索引

CREATE TABLE t2
(
id                        INT NOT NULL,
name                      CHAR(30) NULL,
INDEX SingleIdx(name(20))
); 
此處為給name欄位建立一個名為SingleIdx的單列索引,索引長度為20。
.

4.建立組合索引

組合索引是在多個欄位上建立一個索引

CREATE TABLE t3
(
id                        INT NOT NULL,
name                      CHAR(30) NOT NULL,
age                       INT NOT NULL,                      
info                      VARCHAR(255),
INDEX MultiIdx(id, name, age(100))
);

此處為給id,name和age欄位建立一個名為MultiIdx的組合索引。

組合索引可起幾個索引的作用,但必須遵從“最左字首”:利用索引中最左邊的列集來匹配行。例如這裡又id,name和age 3個欄位構成的索引,索引行中按id/name/age的順序存放,索引可以搜尋下面欄位組合:(id,name,age)、(id,name) 或者 id。如果列不構成索引最左面的字首,MySQL不能使用區域性索引,如(age)或者(name,age)組合則不能使用索引查詢。

5.建立全文索引

FullText(全文索引)可以用於全文搜尋。只有MyISAM儲存引擎支援FullText索引,並且只為CHAR、VARCHAR和TEXT列建立索引。索引總是對整個列進行,不支援區域性(字首)索引。

CREATE TABLE t4
(
id                        INT NOT NULL,
name                      CHAR(30) NOT NULL,
age                       INT NOT NULL,                      
info                      VARCHAR(255),
FULLTEXT INDEX FullTxtIdx(info)
) ENGINE=MyISAM;

此處為給info欄位建立一個名為FullTxtIdx的組合索引,索引長度為20,全文索引非常適合於大型資料集,對於小的資料集,它的用處比較小。

6.建立空間索引

空間索引必須在MyISAM型別的表中建立,且空間型別的欄位必須為非空。

CREATE TABLE t5
(
g                        GEOMETRY NOT NULL,
SPATIAL                  INDEX spatIdx(g)
) ENGINE=MyISAM;

此處為g欄位上建立了名為spatIdx的空間索引,注意建立時指定空間型別欄位值的非空約束,並且表的儲存引擎為MyISAM。

在已經存在的表上建立索引

1.使用 ALTER TABLE 語句建立索引

語法結構:

ALTER TABLE table_name ADD [UNIQUE | FULLTEXT | SPATIAL] [INDEX | KEY]

[index_name] (col_name[length],...) [ASC | DESC]

例如:使用ALTER TABLE在bookname欄位上新增名為BkNameIdx索引,SQL語句如下:

ALTER TABLE book ADD INDEX BkNameIdx(bookname(30));

其他型別的索引只需將INDEX換成其他型別即可如:UNIQUE INDEX,FULLTEXT INDEX,SPATIAL INDEX。

2.使用 CREATE INDEX 建立索引

語法結構:

CREATE [UNIQUE | FULLTEXT | SPATIAL] INDEX index_name

ON table_name (col_name[length],...) [ASC | DESC]

例如:使用CREATE INDEX在bookname欄位上新增名為BkNameIdx索引,SQL語句如下:

CREATE INDEX BkNameIdx ON book(bookname)
其他型別的索引只需將INDEX換成其他型別即可如:UNIQUE INDEX,FULLTEXT INDEX,SPATIAL INDEX。

刪除索引

1.使用ALTER TABLE刪除索引

語法結構:ALTER TABLE table_name DROP INDEX index_name;

2.使用DROP INDEX語句刪除索引

語法結構:DROP INDEX index_name ON table_name;

總結

為資料庫選擇正確的索引是一項複雜的任務,如果索引列較少,則需要的磁碟空間和維護開銷都較少。如果在一個大表上建立了多種組合索引,索引檔案也會膨脹很快。另一方面,索引較多可覆蓋更多的查詢,可能需要實驗若干不同的設計,才能找到最有效的索引。對字串型別的欄位進行索引,如果可能應該指定一個字首長度。例如,如果有個CHAR(255)的列,在前10個或者30個字元內,多數值是惟一的,則不需要對整個列進行索引,短索引不僅可以提高查詢速度而且可以節省磁碟空間、減少I/O操作,因此如何更好的使用索引,還需要我們在實際中多嘗試多測驗。