MySQL優化筆記(三)--索引的使用、原理和設計優化

之前的文章一直在規避索引的建立去優化數據庫,不是不想講,而是這個太重要,必須抽出來講。今天我們就來研究下數據庫索引的設計與優化(MySQL為例)。

文章結構:(1)索引的概述和使用;(2)索引的基本原理;(3)索引分類;(4)索引設計優化

本系列demo下載

(一)MySQL優化筆記(一)--庫與表基本操作以及數據增刪改

(二)MySQL優化筆記(二)--查找優化(1)(非索引設計)

(三)MySQL優化筆記(二)--查找優化(2)(外連接、多表聯合查詢以及查詢注意點)

(四) MySQL優化筆記(三)--索引的使用、原理和設計優化

(五) MySQL優化筆記(四)--表的設計與優化(單表、多表)

(六)MySQL優化筆記(五)--數據庫存儲引擎

(七)MySQL優化筆記(六)--存儲過程和存儲函數

(八)MySQL優化筆記(七)--視圖應用詳解

(九) MySQL優化筆記(八)--鎖機制超詳細解析(鎖分類、事務并發、引擎并發控制)

文章目錄:

(1)索引的概述和使用

概述

-? 什么是索引

-? 索引的優點

-? 索引的缺點

-? 為什么需要索引

索引的使用(語法)

-? 創建索引:(三種方式)

-? 刪除索引

-? 查看索引

(2)索引的基本原理

整體性原理例子

針對存儲性質講解

索引的數據結構:B+tree

B+tree性質

B+tree有兩種搜索方法

(3)索引分類

普通索引

唯一索引

主鍵索引

全文索引:(FULLTEXT)

單列索引與多列索引(其實是相當于一個用法技巧)(重點)

-? 單列索引

-? 多列索引

-? 聚集索引和非聚集索引

-? 聚集索引

-? 非聚集索引

-? 關于聚集索引以及非聚集索引的幾個問題

(4)索引設計優化:

索引建立的幾大原則

索引使用的注意點(大概有14點)

一、索引的概述和使用:

(1)概述:

1)什么是索引?

索引是一種特殊的文件(InnoDB數據表上的索引是表空間的一個組成部分),它們包含著對數據表里所有記錄的引用指針。更通俗的說,數據庫索引好比是一本書前面的目錄,能加快數據庫的查詢速度。在沒有索引的情況下,數據庫會遍歷全部數據后選擇符合條件的;而有了相應的索引之后,數據庫會直接在索引中查找符合條件的選項。

索引是一種特殊的文件(InnoDB數據表上的索引是表空間的一個組成部分),它們包含著對數據表里所有記錄的引用指針。更通俗的說,數據庫索引好比是一本書前面的目錄,能加快數據庫的查詢速度。在沒有索引的情況下,數據庫會遍歷全部數據后選擇符合條件的;而有了相應的索引之后,數據庫會直接在索引中查找符合條件的選項。

索引的性質分類:

索引分為聚簇索引和非聚簇索引兩種,聚簇索引是按照數據存放的物理位置為順序的,而非聚簇索引就不一樣了;聚簇索引能提高多行檢索的速度,而非聚簇索引對于單行的檢索很快。

2)索引的優點:

一】通過創建唯一性索引,可以保證數據庫表中每一行數據的唯一性。

二】可以大大加快數據的檢索速度,這也是創建索引的最主要的原因。

三】可以加速表和表之間的連接,特別是在實現數據的參考完整性方面特別有意義。

四】在使用分組和排序 子句進行數據檢索時,同樣可以顯著減少查詢中分組和排序的時間。

五】通過使用索引,可以在查詢的過程中,使用優化隱藏器,提高系統的性能。

3)索引的缺點:

一】創建索引和維護索引要耗費時間,這種時間隨著數據量的增加而增加。

二】索引需要占物理空間,除了數據表占數據空間之外,每一個索引還要占一定的物理空間,如果要建立聚簇索引,那么需要的空間就會更大。

三】當對表中的數據進行增加、刪除和修改的時候,索引也要動態的維護,這樣就降低了數據的維護速度。

4)為什么需要索引:

數據在磁盤上是以塊的形式存儲的。為確保對磁盤操作的原子性,訪問數據的時候會一并訪問所有數據塊。磁盤上的這些數據塊與鏈表類似,即它們都包含一個數據段和一個指針,指針指向下一個節點(數據塊)的內存地址,而且它們都不需要連續存儲(即邏輯上相鄰的數據塊在物理上可以相隔很遠)。

鑒于很多記錄只能做到按一個字段排序,所以要查詢某個未經排序的字段,就需要使用線性查找,即要訪問N/2個數據塊,其中N指的是一個表所涵蓋的所有數據塊。如果該字段是非鍵字段(也就是說,不包含唯一值),那么就要搜索整個表空間,即要訪問全部N個數據塊。(在某些情況下,索引可以避免排序操作。)

然而,對于經過排序的字段,可以使用二分查找,因此只要訪問log2 N個數據塊。同樣,對于已經排過序的非鍵字段,只要找到更大的值,也就不用再搜索表中的其他數據塊了。這樣一來,性能就會有實質性的提升。

(2)索引的使用(語法):本文代碼實例的針對此數據庫

一】創建索引:(三種方式)

第一種方式:

//第一種方式://在執行CREATE TABLE 時創建索引:(硬設一個id索引)CREATE TABLE `black_list` (? ? `id` BIGINT(20) NOTNULLAUTO_INCREMENT,? ? `black_user_id` BIGINT(20)NULLDEFAULTNULL,? ? `user_id` BIGINT(20)NULLDEFAULTNULL,? ? 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 INDEXindex_name(column_list)//添加普通索引,索引值可出現多次。 ALTER TABLE table_name ADDUNIQUE(column_list)//這條語句創建的索引的值必須是唯一的(除了NULL外,NULL可能會出現多次)。 ALTER TABLE table_name ADD PRIMARYKEY(column_list)//該語句添加一個主鍵,這意味著索引值必須是唯一的,且不能為NULL。ALTER TABLE table_name ADD FULLTEXTindex_name(olumu_name);該語句指定了索引為FULLTEXT,用于全文索引。//針對上述數據庫,增加商品分類的索引ALTER table commodity_list ADD INDEXclassify_index(Classify_Description)

其中table_name是要增加索引的表名,column_list指出對哪些列進行索引,多列時各列之間用逗號分隔。索引名index_name可自己命名,缺省時,MySQL將根據第一個索引列賦一個名稱。另外,ALTER TABLE允許在單個語句中更改多個表,因此可以在同時創建多個索引。

第三種方式:使用CREATE INDEX命令創建

CREATE INDEX可對表增加普通索引或UNIQUE索引。

//標準語句:CREATE INDEX index_name ONtable_name(column_list)CREATE UNIQUE INDEX index_name ONtable_name(column_list)//針對上述數據庫:CREATE INDEX classify_index? ONcommodity_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索引。

如果從表中刪除某列,則索引會受影響。對于多列組合的索引,如果刪除其中的某列,則該列也會從索引中刪除。如果刪除組成索引的所有列,則整個索引將被刪除。

三】查看索引:

SHOWINDEXFROM[table_name];showkeysfrom[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:注釋

二、索引的基本原理:

舉例解析基本原理:

整體性原理例子:

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

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 INDEXindex_name(column_list)CREATE INDEX index_name ONtable_name(column_list);//還有建表的時候創建亦可CREATE TABLEtable_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 NOTNULL,? column_list VARCHAR(16) NOTNULL,? UNIQUE [index_name ]? (column_list(length))? );

三)主鍵索引:

它是一種特殊的唯一索引,不允許有空值。一般是在建表的時候同時創建主鍵索引:

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

四)全文索引:(FULLTEXT)

定義:

全文檢索是對大數據文本進行索引,在建立的索引中對要查找的單詞進行進行搜索,定位哪些文本數據包括要搜索的單詞。因此,全文檢索的全部工作就是建立索引和在索引中搜索定位,所有的工作都是圍繞這兩個來進行的。

此索引關鍵:

建立全文索引中有兩項非常重要,一個是如何對文本進行分詞,一是建立索引的數據結構。分詞的方法基本上是二元分詞法、最大匹配法和統計方法。索引的數據結構基本上采用倒排索引的結構。分詞的好壞關系到查詢的準確程度和生成的索引的大小。

應用:

FULLTEXT索引僅可用于 MyISAM 表;他們可以從CHAR、VARCHAR或TEXT列中作為CREATE TABLE語句的一部分被創建,或是隨后使用ALTER TABLE 或CREATE INDEX被添加。

但是要注意:對于較大的數據集,將你的資料輸入一個沒有FULLTEXT索引的表中,然后創建索引,其速度比把資料輸入現有FULLTEXT索引的速度更為快。不過切記對于大容量的數據表,生成全文索引是一個非常消耗時間非常消耗硬盤空間的做法。因為!!插入修改刪除表的同時也要針對索引做一系列的處理。

創建方法:

//針對content做了全文索引:CREATE TABLE `table` (`id`int(11) NOTNULLAUTO_INCREMENT ,`title`char(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOTNULL,`content` text CHARACTER SET utf8 COLLATE utf8_general_ciNULL,PRIMARY KEY (`id`),FULLTEXT (content));

SQL使用全文索引的方法:首先必須是MyISAM的數據庫引擎的數據表

如果是其他數據引擎,則全文索引不會生效。

SELECT * FROM article WHERE MATCH( content) AGAINST('想查詢的字符串')

此外,MySQL自帶的全文索引只能對英文進行全文檢索,目前無法對中文進行全文檢索。如果需要對包含中文在內的文本數據進行全文檢索,我們需要采用Sphinx(斯芬克斯)/Coreseek技術來處理中文。

注意:

目前,使用MySQL自帶的全文索引時,如果查詢字符串的長度過短將無法得到期望的搜索結果。MySQL全文索引所能找到的詞的默認最小長度為4個字符。另外,如果查詢的字符串包含停止詞,那么該停止詞將會被忽略。

如果可能,請盡量先創建表并插入所有數據后再創建全文索引,而不要在創建表時就直接創建全文索引,因為前者比后者的全文索引效率要高。

五)單列索引與多列索引(其實是相當于一個用法技巧)

單列索引,就是平常的只索引一個一個的字段的方式

//例子為name列的頭10個字符創建一個索引:CREATE TABLEtest(name CHAR(200)NOT NULL,KEYindex_name(name(10)));

多列索引(也叫組合索引)

相關概念(適用多列索引的原因):

MySQL能在多個列上創建索引。一個索引可以由最多15個列組成。(在CHAR和VARCHAR列上,你也可以使用列的前綴作為一個索引的部分)。

一個多重列索引可以認為是包含通過合并(concatenate)索引列值創建的值的一個排序數組。

多個單列索引與單個多列索引的查詢效果不同,因為執行查詢時,MySQL只能使用一個索引,會從多個單列索引中選擇一個限制最為嚴格(獲得結果集記錄數最少)的索引。

當你為在一個WHERE子句索引的第一列指定已知的數量時,MySQL以這種方式使用多重列索引使得查詢非常快速,即使你不為其他列指定值。

適用場景

1.全字段匹配

2.匹配部分最左前綴

3.匹配第一列

4.匹配第一列范圍查詢(可用用like a%,但不能使用like %b)

5.精確匹配某一列和和范圍匹配另外一列

例子:

//假設只使用單列索引名字ALTER TABLE people ADD INDEXname(name);//使用多列索引:ALTER TABLE people ADD INDEXheight_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消耗。這就是為什么主鍵上創建非聚集索引比主鍵上創建聚集索引在插入數據時要快的真正原因。

四、索引設計優化:

(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 idfromt where num isnull

最好不要給數據庫留NULL,盡可能的使用 NOT NULL填充數據庫.

備注、描述、評論之類的可以設置為 NULL,其他的,最好不要使用NULL。

不要以為 NULL 不需要空間,比如:char(100) 型,在字段建立時,空間就固定了, 不管是否插入值(NULL也包含在內),都是占用 100個字符的空間的,如果是varchar這樣的變長字段, null 不占用空間。

可以在num上設置默認值0,確保表中num列沒有null值,然后這樣查詢:

三、)應盡量避免在 where 子句中使用 != 或 <> 操作符,否則將引擎放棄使用索引而進行全表掃描。

四、)應盡量避免在 where 子句中使用 or 來連接條件,如果一個字段有索引,一個字段沒有索引,將導致引擎放棄使用索引而進行全表掃描。如:

select idfromt where num=10orName ='fuzhu'

可以這樣查詢,充分利用索引:

select id from twherenum = 10union allselect id from twhereName ='fuzhu'

五、)in 和 not in 也要慎用,否則會導致全表掃描。

select id from twherenumin(1,2,3)

對于連續的數值,能用 between 就不要用 in 了:

select idfromt where num between1and3

很多時候用 exists 代替 in 是一個好的選擇

select numfroma where numin(select numfromb)

正上面的,用下面的語句替換:

select num from awhereexists(select 1 from bwherenum=a.num)

六、)下面的模糊查詢也將導致全表掃描:

select id from twherename like ‘%abc%’

一般情況下不鼓勵使用like操作,如果非使用不可,如何使用也是一個問題。like “%aaa%” 不會使用索引,而like “aaa%”可以使用索引。

若要提高效率,可以考慮全文檢索。

既然談到模糊查詢下使用索引,我們就順便詳細地講講吧。

1.? like %keyword? ? 索引失效,使用全表掃描。但可以通過翻轉函數+like前模糊查詢+建立翻轉函數索引=走翻轉函數索引,不走全表掃描。例子在此處

2.? like keyword%? ? 索引有效。

3.? like %keyword% 索引失效,也無法使用反向索引。

//可以拿我給出的數據庫試一下嘛。然后用explain測試,就能測出有沒有走索引了select *fromtable where code like'Classify_Description%'select *fromtable where code like'%Classify_Description%'select *fromtable where code like'%Classify_Description'

七、)如果在 where 子句中使用參數,也會導致全表掃描。因為SQL只有在運行時才會解析局部變量,但優化程序不能將訪問計劃的選擇推遲到運行時;它必須在編譯時進行選擇。然 而,如果在編譯時建立訪問計劃,變量的值還是未知的,因而無法作為索引選擇的輸入項。如下面語句將進行全表掃描:

select id from twherenum = @num

可以改為強制查詢使用索引:

select id from twith(index(索引名)) where num=@num

應盡量避免在 where 子句中對字段進行表達式操作,這將導致引擎放棄使用索引而進行全表掃描。如:

select id from twherenum/2 = 100

正上面的應改為:

select id from twherenum = 100*2

八、)應盡量避免在where子句中對字段進行函數操作,這將導致引擎放棄使用索引而進行全表掃描。如:

select id from t wheresubstring(name,1,3)= ’abc’//name以abc開頭的idselect id from t wheredatediff(day,createdate,’2005-11-30′)=0-–‘2005-11-30’//生成的id

應改為:

select idfromt where name like'abc%'select idfromt where createdate >='2005-11-30'andcreatedate <'2005-12-1'

九、).不要在 where 子句中的“=”左邊進行函數、算術運算或其他表達式運算,否則系統將可能無法正確使用索引。

十、)在使用索引字段作為條件時,如果該索引是復合索引(多列索引),那么必須使用到該索引中的第一個字段作為條件時才能保證系統使用該索引,否則該索引將不會被使用,并且應盡可能的讓字段順序與索引順序相一致。

十一、)索引并不是越多越好,索引固然可以提高相應的 select 的效率,但同時也降低了 insert 及 update 的效率,因為 insert 或 update 時有可能會重建索引,所以怎樣建索引需要慎重考慮,視具體情況而定。一個表的索引數最好不要超過6個,若太多則應考慮一些不常使用到的列上建的索引是否有必要。

十二、)應盡可能的避免更新 clustered 索引數據列,因為 clustered 索引數據列的順序就是表記錄的物理存儲順序,一旦該列值改變將導致整個表記錄的順序的調整,會耗費相當大的資源。若應用系統需要頻繁更新 clustered 索引數據列,那么需要考慮是否應將該索引建為 clustered 索引。

十三、)盡量避免向客戶端返回大數據量,若數據量過大,應該考慮相應需求是否合理。

十四、)MySQL查詢只使用一個索引,因此如果where子句中已經使用了索引的話,那么order by中的列是不會使用索引的。因此數據庫默認排序可以符合要求的情況下不要使用排序操作;盡量不要包含多個列的排序,如果需要最好給這些列創建復合索引。

用到的部分例子數據庫在里面

好了,MySQL優化筆記(三)--索引的使用、原理和設計優化,這是積累的必經一步,我會繼續出這個系列文章,分享經驗給大家。歡迎在下面指出錯誤,共同學習!!你的點贊是對我最好的支持!!

作者:JackFrost_fuzhu

鏈接:http://www.lxweimin.com/p/6b080a787b61

來源:簡書

著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,837評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,196評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,688評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,654評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,456評論 6 406
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,955評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,044評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,195評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,725評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,608評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,802評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,318評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,048評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,422評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,673評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,424評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,762評論 2 372

推薦閱讀更多精彩內容