1. 程式人生 > >MySQL優化系列(二)--索引的使用、原理和設計優化

MySQL優化系列(二)--索引的使用、原理和設計優化

一、索引的概述和使用:
(1)概述:
1)什麼是索引?
索引是一種特殊的檔案(InnoDB資料表上的索引是表空間的一個組成部分),它們包含著對資料表裡所有記錄的引用指標。更通俗的說,資料庫索引好比是一本書前面的目錄,能加快資料庫的查詢速度。在沒有索引的情況下,資料庫會遍歷全部資料後選擇符合條件的;而有了相應的索引之後,資料庫會直接在索引中查詢符合條件的選項。
索引是一種特殊的檔案(InnoDB資料表上的索引是表空間的一個組成部分),它們包含著對資料表裡所有記錄的引用指標。更通俗的說,資料庫索引好比是一本書前面的目錄,能加快資料庫的查詢速度。在沒有索引的情況下,資料庫會遍歷全部資料後選擇符合條件的;而有了相應的索引之後,資料庫會直接在索引中查詢符合條件的選項。
索引的性質分類:
索引分為聚簇索引和非聚簇索引兩種,聚簇索引是按照資料存放的物理位置為順序的,而非聚簇索引就不一樣了;聚簇索引能提高多行檢索的速度,而非聚簇索引對於單行的檢索很快。
2)索引的優點:
一】通過建立唯一性索引,可以保證資料庫表中每一行資料的唯一性。
二】可以大大加快資料的檢索速度,這也是建立索引的最主要的原因。
三】可以加速表和表之間的連線,特別是在實現資料的參考完整性方面特別有意義。
四】在使用分組和排序 子句進行資料檢索時,同樣可以顯著減少查詢中分組和排序的時間。
五】通過使用索引,可以在查詢的過程中,使用優化隱藏器,提高系統的效能。
3)索引的缺點:
一】建立索引和維護索引要耗費時間,這種時間隨著資料量的增加而增加。
二】索引需要佔物理空間,除了資料表佔資料空間之外,每一個索引還要佔一定的物理空間,如果要建立聚簇索引,那麼需要的空間就會更大。
三】當對錶中的資料進行增加、刪除和修改的時候,索引也要動態的維護,這樣就降低了資料的維護速度。
4)為什麼需要索引:
資料在磁碟上是以塊的形式儲存的。為確保對磁碟操作的原子性,訪問資料的時候會一併訪問所有資料塊。磁碟上的這些資料塊與連結串列類似,即它們都包含一個數據段和一個指標,指標指向下一個節點(資料塊)的記憶體地址,而且它們都不需要連續儲存(即邏輯上相鄰的資料塊在物理上可以相隔很遠)。
鑑於很多記錄只能做到按一個欄位排序,所以要查詢某個未經排序的欄位,就需要使用線性查詢,即要訪問N/2個數據塊,其中N指的是一個表所涵蓋的所有資料塊。如果該欄位是非鍵欄位(也就是說,不包含唯一值),那麼就要搜尋整個表空間,即要訪問全部N個數據塊。(在某些情況下,索引可以避免排序操作。)
然而,對於經過排序的欄位,可以使用二分查詢,因此只要訪問log2 N個數據塊。同樣,對於已經排過序的非鍵欄位,只要找到更大的值,也就不用再搜尋表中的其他資料塊了。這樣一來,效能就會有實質性的提升。
(2)索引的使用(語法):本文程式碼例項的針對此資料庫
一】建立索引:(三種方式)
第一種方式:
//第一種方式:
//在執行CREATE TABLE 時建立索引:(硬設一個id索引)
CREATE TABLE `black_list` (
    `id` BIGINT(20) NOT NULL AUTO_INCREMENT,
    `black_user_id` BIGINT(20) NULL DEFAULT NULL,
    `user_id` BIGINT(20) NULL DEFAULT NULL,
    PRIMARY KEY (`id`)
    INDEX indexName (black_user_id(length))
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
;
1
2
3
4
5
6
7
8
9
10
11
12
13
第二種方式:使用ALTER TABLE命令去增加索引:
ALTER TABLE用來建立普通索引、UNIQUE索引或PRIMARY KEY索引。
//標準語句:
ALTER TABLE table_name ADD INDEX index_name (column_list)//新增普通索引,索引值可出現多次。 
ALTER TABLE table_name ADD UNIQUE (column_list)//這條語句建立的索引的值必須是唯一的(除了NULL外,NULL可能會出現多次)。 
ALTER TABLE table_name ADD PRIMARY KEY (column_list)//該語句新增一個主鍵,這意味著索引值必須是唯一的,且不能為NULL。
ALTER TABLE table_name ADD FULLTEXT index_name(olumu_name);該語句指定了索引為FULLTEXT,用於全文索引。


//針對上述資料庫,增加商品分類的索引
ALTER table commodity_list ADD INDEX classify_index  (Classify_Description)
1
2
3
4
5
6
7
8
9
其中table_name是要增加索引的表名,column_list指出對哪些列進行索引,多列時各列之間用逗號分隔。索引名index_name可自己命名,預設時,MySQL將根據第一個索引列賦一個名稱。另外,ALTER TABLE允許在單個語句中更改多個表,因此可以在同時建立多個索引。
第三種方式:使用CREATE INDEX命令建立
CREATE INDEX可對錶增加普通索引或UNIQUE索引。
//標準語句:
CREATE INDEX index_name ON table_name (column_list)
CREATE UNIQUE INDEX index_name ON table_name (column_list)
//針對上述資料庫:
CREATE INDEX classify_index  ON commodity_list (Classify_Description)
1
2
3
4
5
table_name、index_name和column_list具有與ALTER TABLE語句中相同的含義,索引名不可選。另外,不能用CREATE INDEX語句建立PRIMARY KEY索引。
二】刪除索引:
刪除索引可以使用ALTER TABLE或DROP INDEX語句來實現。DROP INDEX可以在ALTER TABLE內部作為一條語句處理,其格式如下:
DROP INDEX [indexName] ON [table_name];
alter table [table_name] drop index [index_name] ;
alter table [table_name] drop primary key ;
//針對上述資料庫
drop index classify_index on commodity_list ;
1
2
3
4
5
其中,在前面的兩條語句中,都刪除了table_name中的索引index_name。而在最後一條語句中,只在刪除PRIMARY KEY索引中使用,因為一個表只可能有一個PRIMARY KEY索引,因此不需要指定索引名。如果沒有建立PRIMARY KEY索引,但表具有一個或多個UNIQUE索引,則MySQL將刪除第一個UNIQUE索引。
如果從表中刪除某列,則索引會受影響。對於多列組合的索引,如果刪除其中的某列,則該列也會從索引中刪除。如果刪除組成索引的所有列,則整個索引將被刪除。
三】檢視索引:
SHOW INDEX FROM [table_name];
show keys from [table_name];
1
2


Table:表的名稱。
Non_unique:如果索引不能包括重複詞,則為0。如果可以,則為1。
Key_name:索引的名稱。
Seq_in_index:索引中的列序列號,從1開始。
Column_name:列名稱。
Collation:列以什麼方式儲存在索引中。在MySQL中,有值‘A’(升序)或NULL(無分類)。
Cardinality:索引中唯一值的數目的估計值。通過執行ANALYZE TABLE或myisamchk -a可以更新。基數根據被儲存為整數的統計資料來計數,所以即使對於小型表,該值也沒有必要是精確的。基數越大,當進行聯合時,MySQL使用該索引的機會就越大。
Sub_part:如果列只是被部分地編入索引,則為被編入索引的字元的數目。如果整列被編入索引,則為NULL。
Packed:指示關鍵字如何被壓縮。如果沒有被壓縮,則為NULL。
Null:如果列含有NULL,則含有YES。如果沒有,則該列含有NO。
Index_type:用過的索引方法(BTREE, FULLTEXT, HASH, RTREE)。
Comment:註釋
二、索引的基本原理:
舉例解析基本原理:
整體性原理例子:
除了詞典,生活中隨處可見索引的例子,如火車站的車次表、圖書的目錄等。它們的原理都是一樣的,通過不斷的縮小想要獲得資料的範圍來篩選出最終想要的結果,同時把隨機的事件變成順序的事件,也就是我們總是通過同一種查詢方式來鎖定資料。
SQL的應用場景會使用索引:
資料庫也是一樣,但顯然要複雜許多,因為不僅面臨著等值查詢,還有範圍查詢(>、<、between、in)、模糊查詢(like)、並集查詢(or)等等。資料庫應該選擇怎麼樣的方式來應對所有的問題呢?我們回想字典的例子,能不能把資料分成段,然後分段查詢呢?最簡單的如果1000條資料,1到100分成第一段,101到200分成第二段,201到300分成第三段……這樣查第250條資料,只要找第三段就可以了,一下子去除了90%的無效資料。
針對儲存性質講解:此部分轉載自此博主此部落格
由於儲存介質的特性,磁碟本身存取就比主存慢很多,再加上機械運動耗費,磁碟的存取速度往往是主存的幾百分分之一,因此為了提高效率,要儘量減少磁碟I/O。為了達到這個目的,磁碟往往不是嚴格按需讀取,而是每次都會預讀,即使只需要一個位元組,磁碟也會從這個位置開始,順序向後讀取一定長度的資料放入記憶體。這樣做的理論依據是電腦科學中著名的區域性性原理:當一個數據被用到時,其附近的資料也通常會馬上被使用。程式執行期間所需要的資料通常比較集中。
由於磁碟順序讀取的效率很高(不需要尋道時間,只需很少的旋轉時間),因此對於具有區域性性的程式來說,預讀可以提高I/O效率。
預讀的長度一般為頁(page)的整倍數。頁是計算機管理儲存器的邏輯塊,硬體及作業系統往往將主存和磁碟儲存區分割為連續的大小相等的塊,每個儲存塊稱為一頁(在許多作業系統中,頁得大小通常為4k),主存和磁碟以頁為單位交換資料。當程式要讀取的資料不在主存中時,會觸發一個缺頁異常,此時系統會向磁碟發出讀盤訊號,磁碟會找到資料的起始位置並向後連續讀取一頁或幾頁載入記憶體中,然後異常返回,程式繼續執行。
索引的資料結構:B+tree 此處部分轉載自此博主此部落格


B樹:關於B樹的相關,以後我會詳細更新在此文件
B樹中每個節點包含了鍵值和鍵值對於的資料物件存放地址指標,所以成功搜尋一個物件可以不用到達樹的葉節點。
成功搜尋包括節點內搜尋和沿某一路徑的搜尋,成功搜尋時間取決於關鍵碼所在的層次以及節點內關鍵碼的數量。
在B樹中查詢給定關鍵字的方法是:首先把根結點取來,在根結點所包含的關鍵字K1,…,kj查詢給定的關鍵字(可用順序查詢或二分查詢法),若找到等於給定值的關鍵字,則查詢成功;否則,一定可以確定要查的關鍵字在某個Ki或Ki+1之間,於是取Pi所指的下一層索引節點塊繼續查詢,直到找到,或指標Pi為空時查詢失敗。
B+tree性質:
1.)n棵子tree的節點包含n個關鍵字,不用來儲存資料而是儲存資料的索引。
2.)所有的葉子結點中包含了全部關鍵字的資訊,及指向含這些關鍵字記錄的指標,且葉子結點本身依關鍵字的大小自小而大順序連結。
3.)所有的非終端結點可以看成是索引部分,結點中僅含其子樹中的最大(或最小)關鍵字。
B+樹非葉節點中存放的關鍵碼並不指示資料物件的地址指標,非葉節點只是索引部分。所有的葉節點在同一層上,包含了全部關鍵碼和相應資料物件的存放地址指標,且葉節點按關鍵碼從小到大順序連結。如果實際資料物件按加入的順序儲存而不是按關鍵碼次數儲存的話,葉節點的索引必須是稠密索引,若實際資料儲存按關鍵碼次序存放的話,葉節點索引時稀疏索引。
B+ 樹中,資料物件的插入和刪除僅在葉節點上進行。
B+樹有2個頭指標,一個是樹的根節點,一個是最小關鍵碼的葉節點。
B+tree有兩種搜尋方法:
1)一種是按葉節點自己拉起的連結串列順序搜尋。
2)一種是從根節點開始搜尋,和B樹類似,不過如果非葉節點的關鍵碼等於給定值,搜尋並不停止,而是繼續沿右指標,一直查到葉節點上的關鍵碼。所以無論搜尋是否成功,都將走完樹的所有層。
這兩種處理索引的資料結構的不同之處:(B和B+樹)
1)B樹中同一鍵值不會出現多次,並且它有可能出現在葉結點,也有可能出現在非葉結點中。而B+樹的鍵一定會出現在葉結點中,並且有可能在非葉結點中也有可能重複出現,以維持B+樹的平衡。
2)因為B樹鍵位置不定,且在整個樹結構中只出現一次,雖然可以節省儲存空間,但使得在插入、刪除操作複雜度明顯增加。B+樹相比來說是一種較好的折中。
3)B樹的查詢效率與鍵在樹中的位置有關,最大時間複雜度與B+樹相同(在葉結點的時候),最小時間複雜度為1(在根結點的時候)。而B+樹的時候複雜度對某建成的樹是固定的。


上圖展示了一種可能的索引方式。左邊是資料表,一共有兩列七條記錄,最左邊的是資料記錄的實體地址(注意邏輯上相鄰的記錄在磁碟上也並不是一定物理相鄰的)。為了加快Col2的查詢,可以維護一個右邊所示的二叉查詢樹,每個節點分別包含索引鍵值和一個指向對應資料記錄實體地址的指標,這樣就可以運用二叉查詢在O(log2n)的複雜度內獲取到相應資料。
三、索引分類:
一)普通索引:
基本的索引,它沒有任何限制。
建立方式:
//標準語句:
ALTER TABLE table_name ADD INDEX index_name (column_list)
CREATE INDEX index_name ON table_name (column_list); 
//還有建表的時候建立亦可
CREATE TABLE table_name ( 
ID INT NOT NULL, 
column_listVARCHAR(16) NOT NULL,
INDEX [index_name ] 
(column_list(length)) 
);  
1
2
3
4
5
6
7
8
9
10
11
如果是CHAR,VARCHAR型別,length可以小於欄位實際長度;如果是BLOB和TEXT型別,必須指定 length。
例子:假如length為10,也就是索引這個欄位的記錄的前10個字元。
二)唯一索引:
與前面的普通索引類似,不同的就是:MySQL資料庫索引列的值必須唯一,但允許有空值。如果是組合索引,則列值的組合必須唯一。
它有以下幾種建立方式:
ALTER TABLE table_name ADD UNIQUE (column_list)
CREATE UNIQUE INDEX index_name ON table_name (column_list)
//還有建表時建立
CREATE TABLE table_name (
 ID INT NOT NULL, 
 column_list VARCHAR(16) NOT NULL, 
 UNIQUE [index_name ]  
 (column_list(length)) 
 );  
1
2
3
4
5
6
7
8
9
10
三)主鍵索引:
它是一種特殊的唯一索引,不允許有空值。一般是在建表的時候同時建立主鍵索引:
CREATE TABLE table_name ( 
ID INT NOT NULL,
 [column] VARCHAR(16) NOT NULL,
 PRIMARY KEY(ID)  
 );  
1
2
3
4
5
四)全文索引:(FULLTEXT)
定義:
全文檢索是對大資料文字進行索引,在建立的索引中對要查詢的單詞進行進行搜尋,定位哪些文字資料包括要搜尋的單詞。因此,全文檢索的全部工作就是建立索引和在索引中搜索定位,所有的工作都是圍繞這兩個來進行的。
此索引關鍵:
建立全文索引中有兩項非常重要,一個是如何對文字進行分詞,一是建立索引的資料結構。分詞的方法基本上是二元分詞法、最大匹配法和統計方法。索引的資料結構基本上採用倒排索引的結構。分詞的好壞關係到查詢的準確程度和生成的索引的大小。
應用:
FULLTEXT索引僅可用於 MyISAM 表;他們可以從CHAR、VARCHAR或TEXT列中作為CREATE TABLE語句的一部分被建立,或是隨後使用ALTER TABLE 或CREATE INDEX被新增。
但是要注意:對於較大的資料集,將你的資料輸入一個沒有FULLTEXT索引的表中,然後建立索引,其速度比把資料輸入現有FULLTEXT索引的速度更為快。不過切記對於大容量的資料表,生成全文索引是一個非常消耗時間非常消耗硬碟空間的做法。因為!!插入修改刪除表的同時也要針對索引做一系列的處理。
建立方法:
//針對content做了全文索引:
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 ,
PRIMARY KEY (`id`),
FULLTEXT (content)
);
1
2
3
4
5
6
7
8
SQL使用全文索引的方法:首先必須是MyISAM的資料庫引擎的資料表
如果是其他資料引擎,則全文索引不會生效。
SELECT * FROM article WHERE MATCH( content) AGAINST('想查詢的字串')
1
此外,MySQL自帶的全文索引只能對英文進行全文檢索,目前無法對中文進行全文檢索。如果需要對包含中文在內的文字資料進行全文檢索,我們需要採用Sphinx(斯芬克斯)/Coreseek技術來處理中文。
注意:
目前,使用MySQL自帶的全文索引時,如果查詢字串的長度過短將無法得到期望的搜尋結果。MySQL全文索引所能找到的詞的預設最小長度為4個字元。另外,如果查詢的字串包含停止詞,那麼該停止詞將會被忽略。
如果可能,請儘量先建立表並插入所有資料後再建立全文索引,而不要在建立表時就直接建立全文索引,因為前者比後者的全文索引效率要高。
五)單列索引與多列索引(其實是相當於一個用法技巧)
單列索引,就是平常的只索引一個一個的欄位的方式
//例子為name列的頭10個字元建立一個索引:
CREATE TABLE test (
name CHAR(200) NOT NULL,
KEY index_name (name(10))
);
1
2
3
4
5
多列索引(也叫組合索引):
相關概念(適用多列索引的原因):
MySQL能在多個列上建立索引。一個索引可以由最多15個列組成。(在CHAR和VARCHAR列上,你也可以使用列的字首作為一個索引的部分)。
一個多重列索引可以認為是包含通過合併(concatenate)索引列值建立的值的一個排序陣列。
多個單列索引與單個多列索引的查詢效果不同,因為執行查詢時,MySQL只能使用一個索引,會從多個單列索引中選擇一個限制最為嚴格(獲得結果集記錄數最少)的索引。
當你為在一個WHERE子句索引的第一列指定已知的數量時,MySQL以這種方式使用多重列索引使得查詢非常快速,即使你不為其他列指定值。
適用場景:
1.全欄位匹配
2.匹配部分最左字首
3.匹配第一列
4.匹配第一列範圍查詢(可用用like a%,但不能使用like %b)
5.精確匹配某一列和和範圍匹配另外一列
例子:
//假設只使用單列索引名字
 ALTER TABLE people ADD INDEX name (name);
 //使用多列索引:
  ALTER TABLE people ADD INDEX height_name_age (height,name,age);
  //相當於建立了(height)單列索引,(height,name)組合索引以及(height,name,age)組合索引
/*
注意:
注:在mysql中執行查詢時,只能使用一個索引,如果我們在name,age上分別建索引,執行查詢時,只能使用一個索引,mysql會選擇一個最嚴格(獲得結果集記錄數最少)的索引。
*/
1
2
3
4
5
6
7
8
9
注意:
在建立多列索引時,要根據業務需求,where子句中使用最頻繁的一列放在最左邊。
組合索引(多列索引)的原則:
原則:
最左字首:顧名思義,就是最左優先
平時用的SQL查詢語句一般都有比較多的限制條件,所以為了進一步榨取MySQL的效率,就要考慮建立組合索引(多列索引)。例如上面使用的例子就相當於建立了(height)單列索引,(height,name)組合索引以及(height,name,age)組合索引。
此外,補充一個概念對比,那就是聚集索引和非聚集索引:
1)聚集索引:相關概念說法取自此處
定義:
該索引中鍵值的邏輯順序決定了表中相應行的物理順序。
聚集索引確定表中資料的物理順序。聚集索引類似於電話簿,後者按姓氏排列資料。由於聚集索引規定資料在表中的物理儲存順序,因此一個表只能包含一個聚集索引。但該索引可以包含多個列(組合索引),就像電話簿按姓氏和名字進行組織一樣。
注意事項:
定義聚集索引鍵時使用的列越少越好。
使用的場景:
一)包含大量非重複值的列。
二)使用下列運算子返回一個範圍值的查詢:BETWEEN、>、>=、< 和 <=。
三)被連續訪問的列。
四)返回大型結果集的查詢。
五)經常被使用聯接或 GROUP BY 子句的查詢訪問的列;一般來說,這些是外來鍵列。對 ORDER BY 或 GROUP BY 子句中指定的列進行索引,可以使 SQL Server 不必對資料進行排序,因為這些行已經排序。這樣可以提高查詢效能。
六)OLTP 型別的應用程式,這些程式要求進行非常快速的單行查詢(一般通過主鍵)。
缺點:請看此部落格
不適用於:
頻繁更改的列 。這將導致整行移動(因為 SQL Server 必須按物理順序保留行中的資料值)。這一點要特別注意,因為在大資料量事務處理系統中資料是易失的。
寬鍵 。來自聚集索引的鍵值由所有非聚集索引作為查詢鍵使用,因此儲存在每個非聚集索引的葉條目內。
2)非聚集索引:
定義:
資料儲存在一個地方,索引儲存在另一個地方,索引帶有指標指向資料的儲存位置。
非聚集索引中的專案按索引鍵值的順序儲存,而表中的資訊按另一種順序儲存(這可以由聚集索引規定)。對於非聚集索引,可以為在表非聚集索引中查詢資料時常用的每個列建立一個非聚集索引。有些書籍包含多個索引。例如,一本介紹園藝的書可能會包含一個植物通俗名稱索引,和一個植物學名索引,因為這是讀者查詢資訊的兩種最常用的方法。
兩者的區別此處有個很清晰的例子:請點此處
選擇使用的場景:


關於聚集索引以及非聚集索引的幾個問題:
一)聚集索引的約束是唯一性,是否要求欄位也是唯一的呢?
一般我們指定一個表的主鍵,如果這個表之前沒有聚集索引,同時建立主鍵時候沒有強制指定使用非聚集索引,SQL會預設在此欄位上建立一個聚集索引,而主鍵都是唯一的,所以理所當然的認為建立聚集索引的欄位也需要唯一。
聚集索引可以建立在任何一列你想建立的欄位上,這是從理論上講,實際情況並不能隨便指定,否則在效能上會是惡夢。
二)|主鍵就是聚集索引???
這樣有時會對聚集索引的一種浪費。Innodb將通過主鍵聚集資料,如果沒有定義主鍵,Innodb會選擇第一個非空的唯一索引代替,如果沒有非空唯一索引,Innodb會隱式定義一個6位元組的rowid主鍵來作為聚集索引。innodb只聚集在同一個頁面中的記錄,包含相鄰鍵值的頁面可能會相距甚遠。
因為每個表中只能有一個聚集索引的規則,這使得聚集索引變得更加珍貴。
使用聚集索引的最大好處就是能夠根據查詢要求,迅速縮小查詢範圍,避免全表掃描。在實際應用中,因為 ID號是自動生成的,我們並不知道每條記錄的ID號,所以我們很難在實踐中用ID號來進行查詢。這就使讓ID號這個主鍵作為聚集索引成為一種資源浪費。其次,讓每個ID號都不同的欄位作為聚集索引也不符合“大數目的不同值情況下不應建立聚合索引”規則;當然,這種情況只是針對使用者經常修改記錄內容,特別是索引項的時候會負作用,但對於查詢速度並沒有影響。
三)是不是聚集索引就一定要比非聚集索引效能優呢???
如果想查詢學分在60-90之間的學生的學分以及姓名,在學分上建立聚集索引是否是最優的呢?
答:否。既然只輸出兩列,我們可以在學分以及學生姓名上建立聯合非聚集索引(也就是多列索引),此時的索引就形成了覆蓋索引,即索引所儲存的內容就是最終輸出的資料,這種索引在比以學分為聚集索引做查詢效能更好。
四)在資料庫中通過什麼描述聚集索引與非聚集索引的?
索引是通過二叉樹的形式進行描述的,我們可以這樣區分聚集與非聚集索引的區別:聚集索引的葉節點就是最終的資料節點,而非聚集索引的葉節仍然是索引節點,但它有一個指向最終資料的指標。
五)在主鍵是建立聚集索引的表在資料插入上為什麼比主鍵上建立非聚集索引錶速度要慢?
在有主鍵的表中插入資料行,由於有主鍵唯一性的約束,所以需要保證插入的資料沒有重複。我們來比較下主鍵為聚集索引和非聚集索引的查詢情況:聚集索引由於索引葉節點就是資料頁,所以如果想檢查主鍵的唯一性,需要遍歷所有資料節點才行,但非聚集索引不同,由於非聚集索引上已經包含了主鍵值,所以查詢主鍵唯一性,只需要遍歷所有的索引頁就行,這比遍歷所有資料行減少了不少IO消耗。這就是為什麼主鍵上建立非聚集索引比主鍵上建立聚集索引在插入資料時要快的真正原因。
四、索引設計優化:
(1)索引建立的幾大原則:
1) 最左字首匹配原則,非常重要的原則,mysql會一直向右匹配直到遇到範圍查詢(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)順序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引則都可以用到,a,b,d的順序可以任意調整。
2)=和in可以亂序,比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意順序,mysql的查詢優化器會幫你優化成索引可以識別的形式。
3)儘量選擇區分度高的列作為索引,區分度的公式是count(distinct col)/count(*),表示欄位不重複的比例,比例越大我們掃描的記錄數越少,唯一鍵的區分度是1,而一些狀態、性別欄位可能在大資料面前區分度就是0,那可能有人會問,這個比例有什麼經驗值嗎?使用場景不同,這個值也很難確定,一般需要join的欄位我們都要求是0.1以上,即平均1條掃描10條記錄
4)索引列不能參與計算,保持列“乾淨”,比如from_unixtime(create_time) = ’2014-05-29’就不能使用到索引,原因很簡單,b+樹中存的都是資料表中的欄位值,但進行檢索時,需要把所有元素都應用函式才能比較,顯然成本太大。所以語句應該寫成create_time = unix_timestamp(’2014-05-29’);
5)儘量的擴充套件索引,不要新建索引。比如表中已經有a的索引,現在要加(a,b)的索引,那麼只需要修改原來的索引即可。
6)定義有外來鍵的資料列一定要建立索引。
7)對於那些查詢中很少涉及的列,重複值比較多的列不要建立索引。
8)對於定義為text、image和bit的資料型別的列不要建立索引。
9)對於經常存取的列避免建立索引
(2)索引使用的注意點:
一、)一般說來,索引應建立在那些將用於JOIN,WHERE判斷和ORDER BY排序的欄位上。儘量不要對資料庫中某個含有大量重複的值的欄位建立索引。對於一個ENUM型別的欄位來說,出現大量重複值是很有可能的情況。
二、)應儘量避免在 where 子句中對欄位進行 null 值判斷,否則將導致引擎放棄使用索引而進行全表掃描。如:
select id from t where num is null
1
最好不要給資料庫留NULL,儘可能的使用 NOT NULL填充資料庫.
備註、描述、評論之類的可以設定為 NULL,其他的,最好不要使用NULL。
不要以為 NULL 不需要空間,比如:char(100) 型,在欄位建立時,空間就固定了, 不管是否插入值(NULL也包含在內),都是佔用 100個字元的空間的,如果是varchar這樣的變長欄位, null 不佔用空間。
可以在num上設定預設值0,確保表中num列沒有null值,然後這樣查詢:
三、)應儘量避免在 where 子句中使用 != 或 <> 操作符,否則將引擎放棄使用索引而進行全表掃描。
四、)應儘量避免在 where 子句中使用 or 來連線條件,如果一個欄位有索引,一個欄位沒有索引,將導致引擎放棄使用索引而進行全表掃描。如:
select id from t where num=10 or Name = 'fuzhu'
1
可以這樣查詢,充分利用索引:
select id from t where num = 10
union all
select id from t where Name = 'fuzhu'
1
2
3
五、)in 和 not in 也要慎用,否則會導致全表掃描。
而且負向查詢(not , not in, not like, <>, != ,!>,!< ) 不會使用索引

select id from t where num in(1,2,3)
1
對於連續的數值,能用 between 就不要用 in 了:
select id from t where num between 1 and 3
1
2
很多時候用 exists 代替 in 是一個好的選擇,當然exists也不跑索引。
select num from a where num in(select num from b)
1
2
正上面的,用下面的語句替換:
select num from a where exists(select 1 from b where num=a.num)
1
2
六、)下面的模糊查詢也將導致全表掃描:
select id from t where name like ‘%abc%’
1
2
一般情況下不鼓勵使用like操作,如果非使用不可,如何使用也是一個問題。like “%aaa%” 不會使用索引,而like “aaa%”可以使用索引。
若要提高效率,可以考慮全文檢索。
既然談到模糊查詢下使用索引,我們就順便詳細地講講吧。
1. like %keyword 索引失效,使用全表掃描。但可以通過翻轉函式+like前模糊查詢+建立翻轉函式索引=走翻轉函式索引,不走全表掃描。例子在此處
2. like keyword% 索引有效。
3. like %keyword% 索引失效,也無法使用反向索引。
//可以拿我給出的資料庫試一下嘛。然後用explain測試,就能測出有沒有走索引了
select * from table where code like 'Classify_Description%'  
select * from table where code like '%Classify_Description%'  
select * from table where code like '%Classify_Description'  
1
2
3
4
七、)如果在 where 子句中使用引數,也會導致全表掃描。因為SQL只有在執行時才會解析區域性變數,但優化程式不能將訪問計劃的選擇推遲到執行時;它必須在編譯時進行選擇。然 而,如果在編譯時建立訪問計劃,變數的值還是未知的,因而無法作為索引選擇的輸入項。如下面語句將進行全表掃描:
select id from t where num = @num
1
可以改為強制查詢使用索引:
select id from t with(index(索引名)) where num = @num
1
應儘量避免在 where 子句中對欄位進行表示式操作,這將導致引擎放棄使用索引而進行全表掃描。如:
select id from t where num/2 = 100
1
2
正上面的應改為:
select id from t where num = 100*2
1
2
八、)應儘量避免在where子句中對欄位進行函式操作,這將導致引擎放棄使用索引而進行全表掃描。如:
select id from t where substring(name,1,3) = ’abc’       //name以abc開頭的id
select id from t where datediff(day,createdate,’2005-11-30′) = 0    -–‘2005-11-30’    //生成的id
1
2
應改為:
select id from t where name like 'abc%'
select id from t where createdate >= '2005-11-30' and createdate < '2005-12-1'
1
2
九、).不要在 where 子句中的“=”左邊進行函式、算術運算或其他表示式運算,否則系統將可能無法正確使用索引。
十、)在使用索引欄位作為條件時,如果該索引是複合索引(多列索引),那麼必須使用到該索引中的第一個欄位作為條件時才能保證系統使用該索引,否則該索引將不會被使用,並且應儘可能的讓欄位順序與索引順序相一致。
十一、)索引並不是越多越好,索引固然可以提高相應的 select 的效率,但同時也降低了 insert 及 update 的效率,因為 insert 或 update 時有可能會重建索引,所以怎樣建索引需要慎重考慮,視具體情況而定。一個表的索引數最好不要超過6個,若太多則應考慮一些不常使用到的列上建的索引是否有必要。
十二、)應儘可能的避免更新 clustered 索引資料列,因為 clustered 索引資料列的順序就是表記錄的物理儲存順序,一旦該列值改變將導致整個表記錄的順序的調整,會耗費相當大的資源。若應用系統需要頻繁更新 clustered 索引資料列,那麼需要考慮是否應將該索引建為 clustered 索引。
十三、)儘量避免向客戶端返回大資料量,若資料量過大,應該考慮相應需求是否合理。
十四、)MySQL查詢只使用一個索引,因此如果where子句中已經使用了索引的話,那麼order by中的列是不會使用索引的。因此資料庫預設排序可以符合要求的情況下不要使用排序操作;儘量不要包含多個列的排序,如果需要最好給這些列建立複合索引。