MySQL優化之索引優化

1、索引的概述和使用:
(1)概述:
1)什麼是索引?
  索引是一種特殊的文件(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
;


第二種方式:使用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)
其中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)
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 ;

其中,在前面的兩條語句中,都刪除了table_name中的索引index_name。而在最後一條語句中,只在刪除PRIMARY KEY索引中使用,由於一個表只可能有一個PRIMARY KEY索引,所以不須要指定索引名。若是沒有建立PRIMARY KEY索引,但表具備一個或多個UNIQUE索引,則MySQL將刪除第一個UNIQUE索引。
若是從表中刪除某列,則索引會受影響。對於多列組合的索引,若是刪除其中的某列,則該列也會從索引中刪除。若是刪除組成索引的全部列,則整個索引將被刪除。

三】查看索引:
SHOW INDEX FROM [table_name];
show keys from [table_name];

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:註釋

2、索引的基本原理:
舉例解析基本原理:
總體性原理例子:
除了詞典,生活中隨處可見索引的例子,如火車站的車次表、圖書的目錄等。它們的原理都是同樣的,經過不斷的縮小想要得到數據的範圍來篩選出最終想要的結果,同時把隨機的事件變成順序的事件,也就是咱們老是經過同一種查找方式來鎖定數據。

SQL的應用場景會使用索引:
數據庫也是同樣,但顯然要複雜許多,由於不只面臨着等值查詢,還有範圍查詢(>、<、between、in)、模糊查詢(like)、並集查詢(or)等等。數據庫應該選擇怎麼樣的方式來應對全部的問題呢?咱們回想字典的例子,能不能把數據分紅段,而後分段查詢呢?最簡單的若是1000條數據,1到100分紅第一段,101到200分紅第二段,201到300分紅第三段…這樣查第250條數據,只要找第三段就能夠了,一會兒去除了90%的無效數據。

針對存儲性質講解:
因爲存儲介質的特性,磁盤自己存取就比主存慢不少,再加上機械運動耗費,磁盤的存取速度每每是主存的幾百分分之一,所以爲了提升效率,要儘可能減小磁盤I/O。爲了達到這個目的,磁盤每每不是嚴格按需讀取,而是每次都會預讀,即便只須要一個字節,磁盤也會從這個位置開始,順序向後讀取必定長度的數據放入內存。這樣作的理論依據是計算機科學中著名的局部性原理:當一個數據被用到時,其附近的數據也一般會立刻被使用。程序運行期間所須要的數據一般比較集中。
因爲磁盤順序讀取的效率很高(不須要尋道時間,只需不多的旋轉時間),所以對於具備局部性的程序來講,預讀能夠提升I/O效率。
預讀的長度通常爲頁(page)的整倍數。頁是計算機管理存儲器的邏輯塊,硬件及操做系統每每將主存和磁盤存儲區分割爲連續的大小相等的塊,每一個存儲塊稱爲一頁(在許多操做系統中,頁得大小一般爲4k),主存和磁盤以頁爲單位交換數據。當程序要讀取的數據不在主存中時,會觸發一個缺頁異常,此時系統會向磁盤發出讀盤信號,磁盤會找到數據的起始位置並向後連續讀取一頁或幾頁載入內存中,而後異常返回,程序繼續運行。

索引的數據結構:B+tree
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)的複雜度內獲取到相應數據。

3、索引分類:
一)普通索引:
基本的索引,它沒有任何限制。

建立方式:
//標準語句:
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))
);


若是是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))
);


三)主鍵索引:
它是一種特殊的惟一索引,不容許有空值。通常是在建表的時候同時建立主鍵索引:

CREATE TABLE table_name (
ID INT NOT NULL,
[column] VARCHAR(16) NOT NULL,
PRIMARY KEY(ID)
);

四)全文索引:(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)
);

SQL使用全文索引的方法:首先必須是MyISAM的數據庫引擎的數據表
若是是其餘數據引擎,則全文索引不會生效。

SELECT * FROM article WHERE MATCH( content) AGAINST('想查詢的字符串')
此外,MySQL自帶的全文索引只能對英文進行全文檢索,目前沒法對中文進行全文檢索。若是須要對包含中文在內的文本數據進行全文檢索,咱們須要採用Sphinx(斯芬克斯)/Coreseek技術來處理中文。

注意:
目前,使用MySQL自帶的全文索引時,若是查詢字符串的長度太短將沒法獲得指望的搜索結果。MySQL全文索引所能找到的詞的默認最小長度爲4個字符。另外,若是查詢的字符串包含中止詞,那麼該中止詞將會被忽略。

若是可能,請儘可能先建立表並插入全部數據後再建立全文索引,而不要在建立表時就直接建立全文索引,由於前者比後者的全文索引效率要高。

五)單列索引與多列索引(實際上是至關於一個用法技巧)
單列索引,就是日常的只索引一個一個的字段的方式
//例子爲name列的頭10個字符建立一個索引:
CREATE TABLE test (
name CHAR(200) NOT NULL,
KEY index_name (name(10))
);

多列索引(也叫組合索引):
相關概念(適用多列索引的緣由):
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會選擇一個最嚴格(得到結果集記錄數最少)的索引。
*/

注意:
在建立多列索引時,要根據業務需求,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消耗。這就是爲何主鍵上建立非彙集索引比主鍵上建立彙集索引在插入數據時要快的真正緣由。

4、索引設計優化:
(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

最好不要給數據庫留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'
能夠這樣查詢,充分利用索引:
select id from t where num = 10
union all
select id from t where Name = 'fuzhu'

(五)in 和 not in 也要慎用,不然會致使全表掃描。
並且負向查詢(not , not in, not like, <>, != ,!>,!< ) 不會使用索引

select id from t where num in(1,2,3)

對於連續的數值,能用 between 就不要用 in 了:
select id from t where num between 1 and 3


不少時候用 exists 代替 in 是一個好的選擇,固然exists也不跑索引。
select num from a where num in(select num from b)


正上面的,用下面的語句替換:
select num from a where exists(select 1 from b where num=a.num)


(六)下面的模糊查詢也將致使全表掃描:
select id from t where name like ‘%abc%’


通常狀況下不鼓勵使用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'

(七)若是在 where 子句中使用參數,也會致使全表掃描。
由於SQL只有在運行時纔會解析局部變量,但優化程序不能將訪問計劃的選擇推遲到運行時;它必須在編譯時進行選擇。然 而,若是在編譯時創建訪問計劃,變量的值仍是未知的,於是沒法做爲索引選擇的輸入項。以下面語句將進行全表掃描:

select id from t where num = @num
能夠改成強制查詢使用索引:

select id from t with(index(索引名)) where num = @num
應儘可能避免在 where 子句中對字段進行表達式操做,這將致使引擎放棄使用索引而進行全表掃描。如:

select id from t where num/2 = 100


正上面的應改成:
select id from t where num = 100*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

應改成:
select id from t where name like 'abc%'
select id from t where createdate >= '2005-11-30' and createdate < '2005-12-1'

(九)不要在 where 子句中的「=」左邊進行函數、算術運算或其餘表達式運算,不然系統將可能沒法正確使用索引。
(十)在使用索引字段做爲條件時,若是該索引是複合索引(多列索引),那麼必須使用到該索引中的第一個字段做爲條件時才能保證系統使用該索引,不然該索引將不會被使用,而且應儘量的讓字段順序與索引順序相一致。
(十一)索引並非越多越好
索引當然能夠提升相應的 select 的效率,但同時也下降了 insert 及 update 的效率,由於 insert 或 update 時有可能會重建索引,因此怎樣建索引須要慎重考慮,視具體狀況而定。一個表的索引數最好不要超過6個,若太多則應考慮一些不常使用到的列上建的索引是否有必要。

(十二)應儘量的避免更新 clustered 索引數據列
由於 clustered 索引數據列的順序就是表記錄的物理存儲順序,一旦該列值改變將致使整個表記錄的順序的調整,會耗費至關大的資源。若應用系統須要頻繁更新 clustered 索引數據列,那麼須要考慮是否應將該索引建爲 clustered 索引。
(十三)儘可能避免向客戶端返回大數據量,若數據量過大,應該考慮相應需求是否合理。
(十四)MySQL查詢只使用一個索引,所以若是where子句中已經使用了索引的話,那麼order by中的列是不會使用索引的。
所以數據庫默認排序能夠符合要求的狀況下不要使用排序操做;儘可能不要包含多個列的排序,若是須要最好給這些列建立複合索引。
相關文章
相關標籤/搜索