之前的文章一直在規(guī)避索引的建立去優(yōu)化數(shù)據(jù)庫,不是不想講,而是這個(gè)太重要,必須抽出來講。今天我們就來研究下數(shù)據(jù)庫索引的設(shè)計(jì)與優(yōu)化(MySQL為例)。
文章結(jié)構(gòu):(1)索引的概述和使用;(2)索引的基本原理;(3)索引分類;(4)索引設(shè)計(jì)優(yōu)化
本系列:demo下載
(一)MySQL優(yōu)化筆記(一)--庫與表基本操作以及數(shù)據(jù)增刪改
(二)MySQL優(yōu)化筆記(二)--查找優(yōu)化(1)(非索引設(shè)計(jì))
(三)MySQL優(yōu)化筆記(二)--查找優(yōu)化(2)(外連接、多表聯(lián)合查詢以及查詢注意點(diǎn))
(四) MySQL優(yōu)化筆記(三)--索引的使用、原理和設(shè)計(jì)優(yōu)化
(五) MySQL優(yōu)化筆記(四)--表的設(shè)計(jì)與優(yōu)化(單表、多表)
(六)MySQL優(yōu)化筆記(五)--數(shù)據(jù)庫存儲(chǔ)引擎
(七)MySQL優(yōu)化筆記(六)--存儲(chǔ)過程和存儲(chǔ)函數(shù)
(八)MySQL優(yōu)化筆記(七)--視圖應(yīng)用詳解
(九) MySQL優(yōu)化筆記(八)--鎖機(jī)制超詳細(xì)解析(鎖分類、事務(wù)并發(fā)、引擎并發(fā)控制)
文章目錄:
(1)索引的概述和使用
概述
-? 什么是索引
-? 索引的優(yōu)點(diǎn)
-? 索引的缺點(diǎn)
-? 為什么需要索引
索引的使用(語法)
-? 創(chuàng)建索引:(三種方式)
-? 刪除索引
-? 查看索引
(2)索引的基本原理
整體性原理例子
針對(duì)存儲(chǔ)性質(zhì)講解
索引的數(shù)據(jù)結(jié)構(gòu):B+tree
B+tree性質(zhì)
B+tree有兩種搜索方法
(3)索引分類
普通索引
唯一索引
主鍵索引
全文索引:(FULLTEXT)
單列索引與多列索引(其實(shí)是相當(dāng)于一個(gè)用法技巧)(重點(diǎn))
-? 單列索引
-? 多列索引
-? 聚集索引和非聚集索引
-? 聚集索引
-? 非聚集索引
-? 關(guān)于聚集索引以及非聚集索引的幾個(gè)問題
(4)索引設(shè)計(jì)優(yōu)化:
索引建立的幾大原則
索引使用的注意點(diǎn)(大概有14點(diǎn))
一、索引的概述和使用:
(1)概述:
1)什么是索引?
索引是一種特殊的文件(InnoDB數(shù)據(jù)表上的索引是表空間的一個(gè)組成部分),它們包含著對(duì)數(shù)據(jù)表里所有記錄的引用指針。更通俗的說,數(shù)據(jù)庫索引好比是一本書前面的目錄,能加快數(shù)據(jù)庫的查詢速度。在沒有索引的情況下,數(shù)據(jù)庫會(huì)遍歷全部數(shù)據(jù)后選擇符合條件的;而有了相應(yīng)的索引之后,數(shù)據(jù)庫會(huì)直接在索引中查找符合條件的選項(xiàng)。
索引是一種特殊的文件(InnoDB數(shù)據(jù)表上的索引是表空間的一個(gè)組成部分),它們包含著對(duì)數(shù)據(jù)表里所有記錄的引用指針。更通俗的說,數(shù)據(jù)庫索引好比是一本書前面的目錄,能加快數(shù)據(jù)庫的查詢速度。在沒有索引的情況下,數(shù)據(jù)庫會(huì)遍歷全部數(shù)據(jù)后選擇符合條件的;而有了相應(yīng)的索引之后,數(shù)據(jù)庫會(huì)直接在索引中查找符合條件的選項(xiàng)。
索引的性質(zhì)分類:
索引分為聚簇索引和非聚簇索引兩種,聚簇索引是按照數(shù)據(jù)存放的物理位置為順序的,而非聚簇索引就不一樣了;聚簇索引能提高多行檢索的速度,而非聚簇索引對(duì)于單行的檢索很快。
2)索引的優(yōu)點(diǎn):
一】通過創(chuàng)建唯一性索引,可以保證數(shù)據(jù)庫表中每一行數(shù)據(jù)的唯一性。
二】可以大大加快數(shù)據(jù)的檢索速度,這也是創(chuàng)建索引的最主要的原因。
三】可以加速表和表之間的連接,特別是在實(shí)現(xiàn)數(shù)據(jù)的參考完整性方面特別有意義。
四】在使用分組和排序 子句進(jìn)行數(shù)據(jù)檢索時(shí),同樣可以顯著減少查詢中分組和排序的時(shí)間。
五】通過使用索引,可以在查詢的過程中,使用優(yōu)化隱藏器,提高系統(tǒng)的性能。
3)索引的缺點(diǎn):
一】創(chuàng)建索引和維護(hù)索引要耗費(fèi)時(shí)間,這種時(shí)間隨著數(shù)據(jù)量的增加而增加。
二】索引需要占物理空間,除了數(shù)據(jù)表占數(shù)據(jù)空間之外,每一個(gè)索引還要占一定的物理空間,如果要建立聚簇索引,那么需要的空間就會(huì)更大。
三】當(dāng)對(duì)表中的數(shù)據(jù)進(jìn)行增加、刪除和修改的時(shí)候,索引也要?jiǎng)討B(tài)的維護(hù),這樣就降低了數(shù)據(jù)的維護(hù)速度。
4)為什么需要索引:
數(shù)據(jù)在磁盤上是以塊的形式存儲(chǔ)的。為確保對(duì)磁盤操作的原子性,訪問數(shù)據(jù)的時(shí)候會(huì)一并訪問所有數(shù)據(jù)塊。磁盤上的這些數(shù)據(jù)塊與鏈表類似,即它們都包含一個(gè)數(shù)據(jù)段和一個(gè)指針,指針指向下一個(gè)節(jié)點(diǎn)(數(shù)據(jù)塊)的內(nèi)存地址,而且它們都不需要連續(xù)存儲(chǔ)(即邏輯上相鄰的數(shù)據(jù)塊在物理上可以相隔很遠(yuǎn))。
鑒于很多記錄只能做到按一個(gè)字段排序,所以要查詢某個(gè)未經(jīng)排序的字段,就需要使用線性查找,即要訪問N/2個(gè)數(shù)據(jù)塊,其中N指的是一個(gè)表所涵蓋的所有數(shù)據(jù)塊。如果該字段是非鍵字段(也就是說,不包含唯一值),那么就要搜索整個(gè)表空間,即要訪問全部N個(gè)數(shù)據(jù)塊。(在某些情況下,索引可以避免排序操作。)
然而,對(duì)于經(jīng)過排序的字段,可以使用二分查找,因此只要訪問log2 N個(gè)數(shù)據(jù)塊。同樣,對(duì)于已經(jīng)排過序的非鍵字段,只要找到更大的值,也就不用再搜索表中的其他數(shù)據(jù)塊了。這樣一來,性能就會(huì)有實(shí)質(zhì)性的提升。
(2)索引的使用(語法):本文代碼實(shí)例的針對(duì)此數(shù)據(jù)庫
一】創(chuàng)建索引:(三種方式)
第一種方式:
//第一種方式://在執(zhí)行CREATE TABLE 時(shí)創(chuàng)建索引:(硬設(shè)一個(gè)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用來創(chuàng)建普通索引、UNIQUE索引或PRIMARY KEY索引。
//標(biāo)準(zhǔn)語句:ALTER TABLE table_name ADD INDEXindex_name(column_list)//添加普通索引,索引值可出現(xiàn)多次。 ALTER TABLE table_name ADDUNIQUE(column_list)//這條語句創(chuàng)建的索引的值必須是唯一的(除了NULL外,NULL可能會(huì)出現(xiàn)多次)。 ALTER TABLE table_name ADD PRIMARYKEY(column_list)//該語句添加一個(gè)主鍵,這意味著索引值必須是唯一的,且不能為NULL。ALTER TABLE table_name ADD FULLTEXTindex_name(olumu_name);該語句指定了索引為FULLTEXT,用于全文索引。//針對(duì)上述數(shù)據(jù)庫,增加商品分類的索引ALTER table commodity_list ADD INDEXclassify_index(Classify_Description)
其中table_name是要增加索引的表名,column_list指出對(duì)哪些列進(jìn)行索引,多列時(shí)各列之間用逗號(hào)分隔。索引名index_name可自己命名,缺省時(shí),MySQL將根據(jù)第一個(gè)索引列賦一個(gè)名稱。另外,ALTER TABLE允許在單個(gè)語句中更改多個(gè)表,因此可以在同時(shí)創(chuàng)建多個(gè)索引。
第三種方式:使用CREATE INDEX命令創(chuàng)建
CREATE INDEX可對(duì)表增加普通索引或UNIQUE索引。
//標(biāo)準(zhǔn)語句:CREATE INDEX index_name ONtable_name(column_list)CREATE UNIQUE INDEX index_name ONtable_name(column_list)//針對(duì)上述數(shù)據(jù)庫:CREATE INDEX classify_index? ONcommodity_list(Classify_Description)
table_name、index_name和column_list具有與ALTER TABLE語句中相同的含義,索引名不可選。另外,不能用CREATE INDEX語句創(chuàng)建PRIMARY KEY索引。
二】刪除索引:
刪除索引可以使用ALTER TABLE或DROP INDEX語句來實(shí)現(xiàn)。DROP INDEX可以在ALTER TABLE內(nèi)部作為一條語句處理,其格式如下:
DROP INDEX [indexName] ON [table_name];alter table [table_name] drop index [index_name] ;alter table [table_name] drop primary key ;//針對(duì)上述數(shù)據(jù)庫drop index classify_index on commodity_list ;
其中,在前面的兩條語句中,都刪除了table_name中的索引index_name。而在最后一條語句中,只在刪除PRIMARY KEY索引中使用,因?yàn)橐粋€(gè)表只可能有一個(gè)PRIMARY KEY索引,因此不需要指定索引名。如果沒有創(chuàng)建PRIMARY KEY索引,但表具有一個(gè)或多個(gè)UNIQUE索引,則MySQL將刪除第一個(gè)UNIQUE索引。
如果從表中刪除某列,則索引會(huì)受影響。對(duì)于多列組合的索引,如果刪除其中的某列,則該列也會(huì)從索引中刪除。如果刪除組成索引的所有列,則整個(gè)索引將被刪除。
三】查看索引:
SHOWINDEXFROM[table_name];showkeysfrom[table_name];
這里寫圖片描述
Table:表的名稱。
Non_unique:如果索引不能包括重復(fù)詞,則為0。如果可以,則為1。
Key_name:索引的名稱。
Seq_in_index:索引中的列序列號(hào),從1開始。
Column_name:列名稱。
Collation:列以什么方式存儲(chǔ)在索引中。在MySQL中,有值‘A'(升序)或NULL(無分類)。
Cardinality:索引中唯一值的數(shù)目的估計(jì)值。通過運(yùn)行ANALYZE TABLE或myisamchk -a可以更新。基數(shù)根據(jù)被存儲(chǔ)為整數(shù)的統(tǒng)計(jì)數(shù)據(jù)來計(jì)數(shù),所以即使對(duì)于小型表,該值也沒有必要是精確的。基數(shù)越大,當(dāng)進(jìn)行聯(lián)合時(shí),MySQL使用該索引的機(jī)會(huì)就越大。
Sub_part:如果列只是被部分地編入索引,則為被編入索引的字符的數(shù)目。如果整列被編入索引,則為NULL。
Packed:指示關(guān)鍵字如何被壓縮。如果沒有被壓縮,則為NULL。
Null:如果列含有NULL,則含有YES。如果沒有,則該列含有NO。
Index_type:用過的索引方法(BTREE, FULLTEXT, HASH, RTREE)。
Comment:注釋
二、索引的基本原理:
舉例解析基本原理:
整體性原理例子:
除了詞典,生活中隨處可見索引的例子,如火車站的車次表、圖書的目錄等。它們的原理都是一樣的,通過不斷的縮小想要獲得數(shù)據(jù)的范圍來篩選出最終想要的結(jié)果,同時(shí)把隨機(jī)的事件變成順序的事件,也就是我們總是通過同一種查找方式來鎖定數(shù)據(jù)。
SQL的應(yīng)用場(chǎng)景會(huì)使用索引:
數(shù)據(jù)庫也是一樣,但顯然要復(fù)雜許多,因?yàn)椴粌H面臨著等值查詢,還有范圍查詢(>、<、between、in)、模糊查詢(like)、并集查詢(or)等等。數(shù)據(jù)庫應(yīng)該選擇怎么樣的方式來應(yīng)對(duì)所有的問題呢?我們回想字典的例子,能不能把數(shù)據(jù)分成段,然后分段查詢呢?最簡(jiǎn)單的如果1000條數(shù)據(jù),1到100分成第一段,101到200分成第二段,201到300分成第三段......這樣查第250條數(shù)據(jù),只要找第三段就可以了,一下子去除了90%的無效數(shù)據(jù)。
針對(duì)存儲(chǔ)性質(zhì)講解:此部分轉(zhuǎn)載自此博主此博客
由于存儲(chǔ)介質(zhì)的特性,磁盤本身存取就比主存慢很多,再加上機(jī)械運(yùn)動(dòng)耗費(fèi),磁盤的存取速度往往是主存的幾百分分之一,因此為了提高效率,要盡量減少磁盤I/O。為了達(dá)到這個(gè)目的,磁盤往往不是嚴(yán)格按需讀取,而是每次都會(huì)預(yù)讀,即使只需要一個(gè)字節(jié),磁盤也會(huì)從這個(gè)位置開始,順序向后讀取一定長度的數(shù)據(jù)放入內(nèi)存。這樣做的理論依據(jù)是計(jì)算機(jī)科學(xué)中著名的局部性原理:當(dāng)一個(gè)數(shù)據(jù)被用到時(shí),其附近的數(shù)據(jù)也通常會(huì)馬上被使用。程序運(yùn)行期間所需要的數(shù)據(jù)通常比較集中。
由于磁盤順序讀取的效率很高(不需要尋道時(shí)間,只需很少的旋轉(zhuǎn)時(shí)間),因此對(duì)于具有局部性的程序來說,預(yù)讀可以提高I/O效率。
預(yù)讀的長度一般為頁(page)的整倍數(shù)。頁是計(jì)算機(jī)管理存儲(chǔ)器的邏輯塊,硬件及操作系統(tǒng)往往將主存和磁盤存儲(chǔ)區(qū)分割為連續(xù)的大小相等的塊,每個(gè)存儲(chǔ)塊稱為一頁(在許多操作系統(tǒng)中,頁得大小通常為4k),主存和磁盤以頁為單位交換數(shù)據(jù)。當(dāng)程序要讀取的數(shù)據(jù)不在主存中時(shí),會(huì)觸發(fā)一個(gè)缺頁異常,此時(shí)系統(tǒng)會(huì)向磁盤發(fā)出讀盤信號(hào),磁盤會(huì)找到數(shù)據(jù)的起始位置并向后連續(xù)讀取一頁或幾頁載入內(nèi)存中,然后異常返回,程序繼續(xù)運(yùn)行。
索引的數(shù)據(jù)結(jié)構(gòu):B+tree此處部分轉(zhuǎn)載自此博主此博客
這里寫圖片描述
B樹:關(guān)于B樹的相關(guān),以后我會(huì)詳細(xì)更新在此文檔
B樹中每個(gè)節(jié)點(diǎn)包含了鍵值和鍵值對(duì)于的數(shù)據(jù)對(duì)象存放地址指針,所以成功搜索一個(gè)對(duì)象可以不用到達(dá)樹的葉節(jié)點(diǎn)。
成功搜索包括節(jié)點(diǎn)內(nèi)搜索和沿某一路徑的搜索,成功搜索時(shí)間取決于關(guān)鍵碼所在的層次以及節(jié)點(diǎn)內(nèi)關(guān)鍵碼的數(shù)量。
在B樹中查找給定關(guān)鍵字的方法是:首先把根結(jié)點(diǎn)取來,在根結(jié)點(diǎn)所包含的關(guān)鍵字K1,…,kj查找給定的關(guān)鍵字(可用順序查找或二分查找法),若找到等于給定值的關(guān)鍵字,則查找成功;否則,一定可以確定要查的關(guān)鍵字在某個(gè)Ki或Ki+1之間,于是取Pi所指的下一層索引節(jié)點(diǎn)塊繼續(xù)查找,直到找到,或指針Pi為空時(shí)查找失敗。
B+tree性質(zhì):
1.)n棵子tree的節(jié)點(diǎn)包含n個(gè)關(guān)鍵字,不用來保存數(shù)據(jù)而是保存數(shù)據(jù)的索引。
2.)所有的葉子結(jié)點(diǎn)中包含了全部關(guān)鍵字的信息,及指向含這些關(guān)鍵字記錄的指針,且葉子結(jié)點(diǎn)本身依關(guān)鍵字的大小自小而大順序鏈接。
3.)所有的非終端結(jié)點(diǎn)可以看成是索引部分,結(jié)點(diǎn)中僅含其子樹中的最大(或最小)關(guān)鍵字。
B+樹非葉節(jié)點(diǎn)中存放的關(guān)鍵碼并不指示數(shù)據(jù)對(duì)象的地址指針,非葉節(jié)點(diǎn)只是索引部分。所有的葉節(jié)點(diǎn)在同一層上,包含了全部關(guān)鍵碼和相應(yīng)數(shù)據(jù)對(duì)象的存放地址指針,且葉節(jié)點(diǎn)按關(guān)鍵碼從小到大順序鏈接。如果實(shí)際數(shù)據(jù)對(duì)象按加入的順序存儲(chǔ)而不是按關(guān)鍵碼次數(shù)存儲(chǔ)的話,葉節(jié)點(diǎn)的索引必須是稠密索引,若實(shí)際數(shù)據(jù)存儲(chǔ)按關(guān)鍵碼次序存放的話,葉節(jié)點(diǎn)索引時(shí)稀疏索引。
B+ 樹中,數(shù)據(jù)對(duì)象的插入和刪除僅在葉節(jié)點(diǎn)上進(jìn)行。
B+樹有2個(gè)頭指針,一個(gè)是樹的根節(jié)點(diǎn),一個(gè)是最小關(guān)鍵碼的葉節(jié)點(diǎn)。
B+tree有兩種搜索方法:
1)一種是按葉節(jié)點(diǎn)自己拉起的鏈表順序搜索。
2)一種是從根節(jié)點(diǎn)開始搜索,和B樹類似,不過如果非葉節(jié)點(diǎn)的關(guān)鍵碼等于給定值,搜索并不停止,而是繼續(xù)沿右指針,一直查到葉節(jié)點(diǎn)上的關(guān)鍵碼。所以無論搜索是否成功,都將走完樹的所有層。
這兩種處理索引的數(shù)據(jù)結(jié)構(gòu)的不同之處:(B和B+樹)
1)B樹中同一鍵值不會(huì)出現(xiàn)多次,并且它有可能出現(xiàn)在葉結(jié)點(diǎn),也有可能出現(xiàn)在非葉結(jié)點(diǎn)中。而B+樹的鍵一定會(huì)出現(xiàn)在葉結(jié)點(diǎn)中,并且有可能在非葉結(jié)點(diǎn)中也有可能重復(fù)出現(xiàn),以維持B+樹的平衡。
2)因?yàn)锽樹鍵位置不定,且在整個(gè)樹結(jié)構(gòu)中只出現(xiàn)一次,雖然可以節(jié)省存儲(chǔ)空間,但使得在插入、刪除操作復(fù)雜度明顯增加。B+樹相比來說是一種較好的折中。
3)B樹的查詢效率與鍵在樹中的位置有關(guān),最大時(shí)間復(fù)雜度與B+樹相同(在葉結(jié)點(diǎn)的時(shí)候),最小時(shí)間復(fù)雜度為1(在根結(jié)點(diǎn)的時(shí)候)。而B+樹的時(shí)候復(fù)雜度對(duì)某建成的樹是固定的。
這里寫圖片描述
上圖展示了一種可能的索引方式。左邊是數(shù)據(jù)表,一共有兩列七條記錄,最左邊的是數(shù)據(jù)記錄的物理地址(注意邏輯上相鄰的記錄在磁盤上也并不是一定物理相鄰的)。為了加快Col2的查找,可以維護(hù)一個(gè)右邊所示的二叉查找樹,每個(gè)節(jié)點(diǎn)分別包含索引鍵值和一個(gè)指向?qū)?yīng)數(shù)據(jù)記錄物理地址的指針,這樣就可以運(yùn)用二叉查找在O(log2n)的復(fù)雜度內(nèi)獲取到相應(yīng)數(shù)據(jù)。
三、索引分類:
一)普通索引:
基本的索引,它沒有任何限制。
創(chuàng)建方式:
//標(biāo)準(zhǔn)語句:ALTER TABLE table_name ADD INDEXindex_name(column_list)CREATE INDEX index_name ONtable_name(column_list);//還有建表的時(shí)候創(chuàng)建亦可CREATE TABLEtable_name( ID INT NOT NULL, column_listVARCHAR(16)NOT NULL,INDEX [index_name ](column_list(length)) );
如果是CHAR,VARCHAR類型,length可以小于字段實(shí)際長度;如果是BLOB和TEXT類型,必須指定 length。
例子:假如length為10,也就是索引這個(gè)字段的記錄的前10個(gè)字符。
二)唯一索引:
與前面的普通索引類似,不同的就是:MySQL數(shù)據(jù)庫索引列的值必須唯一,但允許有空值。如果是組合索引,則列值的組合必須唯一。
它有以下幾種創(chuàng)建方式:
ALTER TABLE table_name ADD UNIQUE (column_list)CREATE UNIQUE INDEX index_name ON table_name (column_list)//還有建表時(shí)創(chuàng)建CREATE TABLE table_name ( ID INT NOTNULL,? column_list VARCHAR(16) NOTNULL,? UNIQUE [index_name ]? (column_list(length))? );
三)主鍵索引:
它是一種特殊的唯一索引,不允許有空值。一般是在建表的時(shí)候同時(shí)創(chuàng)建主鍵索引:
CREATE TABLE table_name ( ID INT NOTNULL, [column] VARCHAR(16) NOTNULL, PRIMARY KEY(ID)? );
四)全文索引:(FULLTEXT)
定義:
全文檢索是對(duì)大數(shù)據(jù)文本進(jìn)行索引,在建立的索引中對(duì)要查找的單詞進(jìn)行進(jìn)行搜索,定位哪些文本數(shù)據(jù)包括要搜索的單詞。因此,全文檢索的全部工作就是建立索引和在索引中搜索定位,所有的工作都是圍繞這兩個(gè)來進(jìn)行的。
此索引關(guān)鍵:
建立全文索引中有兩項(xiàng)非常重要,一個(gè)是如何對(duì)文本進(jìn)行分詞,一是建立索引的數(shù)據(jù)結(jié)構(gòu)。分詞的方法基本上是二元分詞法、最大匹配法和統(tǒng)計(jì)方法。索引的數(shù)據(jù)結(jié)構(gòu)基本上采用倒排索引的結(jié)構(gòu)。分詞的好壞關(guān)系到查詢的準(zhǔn)確程度和生成的索引的大小。
應(yīng)用:
FULLTEXT索引僅可用于 MyISAM 表;他們可以從CHAR、VARCHAR或TEXT列中作為CREATE TABLE語句的一部分被創(chuàng)建,或是隨后使用ALTER TABLE 或CREATE INDEX被添加。
但是要注意:對(duì)于較大的數(shù)據(jù)集,將你的資料輸入一個(gè)沒有FULLTEXT索引的表中,然后創(chuàng)建索引,其速度比把資料輸入現(xiàn)有FULLTEXT索引的速度更為快。不過切記對(duì)于大容量的數(shù)據(jù)表,生成全文索引是一個(gè)非常消耗時(shí)間非常消耗硬盤空間的做法。因?yàn)椋。〔迦胄薷膭h除表的同時(shí)也要針對(duì)索引做一系列的處理。
創(chuàng)建方法:
//針對(duì)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的數(shù)據(jù)庫引擎的數(shù)據(jù)表
如果是其他數(shù)據(jù)引擎,則全文索引不會(huì)生效。
SELECT * FROM article WHERE MATCH( content) AGAINST('想查詢的字符串')
此外,MySQL自帶的全文索引只能對(duì)英文進(jìn)行全文檢索,目前無法對(duì)中文進(jìn)行全文檢索。如果需要對(duì)包含中文在內(nèi)的文本數(shù)據(jù)進(jìn)行全文檢索,我們需要采用Sphinx(斯芬克斯)/Coreseek技術(shù)來處理中文。
注意:
目前,使用MySQL自帶的全文索引時(shí),如果查詢字符串的長度過短將無法得到期望的搜索結(jié)果。MySQL全文索引所能找到的詞的默認(rèn)最小長度為4個(gè)字符。另外,如果查詢的字符串包含停止詞,那么該停止詞將會(huì)被忽略。
如果可能,請(qǐng)盡量先創(chuàng)建表并插入所有數(shù)據(jù)后再創(chuàng)建全文索引,而不要在創(chuàng)建表時(shí)就直接創(chuàng)建全文索引,因?yàn)榍罢弑群笳叩娜乃饕室摺?/p>
五)單列索引與多列索引(其實(shí)是相當(dāng)于一個(gè)用法技巧)
單列索引,就是平常的只索引一個(gè)一個(gè)的字段的方式
//例子為name列的頭10個(gè)字符創(chuàng)建一個(gè)索引:CREATE TABLEtest(name CHAR(200)NOT NULL,KEYindex_name(name(10)));
多列索引(也叫組合索引):
相關(guān)概念(適用多列索引的原因):
MySQL能在多個(gè)列上創(chuàng)建索引。一個(gè)索引可以由最多15個(gè)列組成。(在CHAR和VARCHAR列上,你也可以使用列的前綴作為一個(gè)索引的部分)。
一個(gè)多重列索引可以認(rèn)為是包含通過合并(concatenate)索引列值創(chuàng)建的值的一個(gè)排序數(shù)組。
多個(gè)單列索引與單個(gè)多列索引的查詢效果不同,因?yàn)閳?zhí)行查詢時(shí),MySQL只能使用一個(gè)索引,會(huì)從多個(gè)單列索引中選擇一個(gè)限制最為嚴(yán)格(獲得結(jié)果集記錄數(shù)最少)的索引。
當(dāng)你為在一個(gè)WHERE子句索引的第一列指定已知的數(shù)量時(shí),MySQL以這種方式使用多重列索引使得查詢非常快速,即使你不為其他列指定值。
適用場(chǎng)景:
1.全字段匹配
2.匹配部分最左前綴
3.匹配第一列
4.匹配第一列范圍查詢(可用用like a%,但不能使用like %b)
5.精確匹配某一列和和范圍匹配另外一列
例子:
//假設(shè)只使用單列索引名字ALTER TABLE people ADD INDEXname(name);//使用多列索引:ALTER TABLE people ADD INDEXheight_name_age(height,name,age);//相當(dāng)于創(chuàng)建了(height)單列索引,(height,name)組合索引以及(height,name,age)組合索引/*
注意:
注:在mysql中執(zhí)行查詢時(shí),只能使用一個(gè)索引,如果我們?cè)趎ame,age上分別建索引,執(zhí)行查詢時(shí),只能使用一個(gè)索引,mysql會(huì)選擇一個(gè)最嚴(yán)格(獲得結(jié)果集記錄數(shù)最少)的索引。
*/
注意:
在創(chuàng)建多列索引時(shí),要根據(jù)業(yè)務(wù)需求,where子句中使用最頻繁的一列放在最左邊。
組合索引(多列索引)的原則:
原則:
最左前綴:顧名思義,就是最左優(yōu)先
平時(shí)用的SQL查詢語句一般都有比較多的限制條件,所以為了進(jìn)一步榨取MySQL的效率,就要考慮建立組合索引(多列索引)。例如上面使用的例子就相當(dāng)于創(chuàng)建了(height)單列索引,(height,name)組合索引以及(height,name,age)組合索引。
此外,補(bǔ)充一個(gè)概念對(duì)比,那就是聚集索引和非聚集索引:
1)聚集索引:相關(guān)概念說法取自此處
定義:
該索引中鍵值的邏輯順序決定了表中相應(yīng)行的物理順序。
聚集索引確定表中數(shù)據(jù)的物理順序。聚集索引類似于電話簿,后者按姓氏排列數(shù)據(jù)。由于聚集索引規(guī)定數(shù)據(jù)在表中的物理存儲(chǔ)順序,因此一個(gè)表只能包含一個(gè)聚集索引。但該索引可以包含多個(gè)列(組合索引),就像電話簿按姓氏和名字進(jìn)行組織一樣。
注意事項(xiàng):
定義聚集索引鍵時(shí)使用的列越少越好。
使用的場(chǎng)景:
一)包含大量非重復(fù)值的列。
二)使用下列運(yùn)算符返回一個(gè)范圍值的查詢:BETWEEN、>、>=、< 和 <=。
三)被連續(xù)訪問的列。
四)返回大型結(jié)果集的查詢。
五)經(jīng)常被使用聯(lián)接或 GROUP BY 子句的查詢?cè)L問的列;一般來說,這些是外鍵列。對(duì) ORDER BY 或 GROUP BY 子句中指定的列進(jìn)行索引,可以使 SQL Server 不必對(duì)數(shù)據(jù)進(jìn)行排序,因?yàn)檫@些行已經(jīng)排序。這樣可以提高查詢性能。
六)OLTP 類型的應(yīng)用程序,這些程序要求進(jìn)行非常快速的單行查找(一般通過主鍵)。
缺點(diǎn):請(qǐng)看此博客
不適用于:
頻繁更改的列 。這將導(dǎo)致整行移動(dòng)(因?yàn)?SQL Server 必須按物理順序保留行中的數(shù)據(jù)值)。這一點(diǎn)要特別注意,因?yàn)樵诖髷?shù)據(jù)量事務(wù)處理系統(tǒng)中數(shù)據(jù)是易失的。
寬鍵 。來自聚集索引的鍵值由所有非聚集索引作為查找鍵使用,因此存儲(chǔ)在每個(gè)非聚集索引的葉條目內(nèi)。
2)非聚集索引:
定義:
數(shù)據(jù)存儲(chǔ)在一個(gè)地方,索引存儲(chǔ)在另一個(gè)地方,索引帶有指針指向數(shù)據(jù)的存儲(chǔ)位置。
非聚集索引中的項(xiàng)目按索引鍵值的順序存儲(chǔ),而表中的信息按另一種順序存儲(chǔ)(這可以由聚集索引規(guī)定)。對(duì)于非聚集索引,可以為在表非聚集索引中查找數(shù)據(jù)時(shí)常用的每個(gè)列創(chuàng)建一個(gè)非聚集索引。有些書籍包含多個(gè)索引。例如,一本介紹園藝的書可能會(huì)包含一個(gè)植物通俗名稱索引,和一個(gè)植物學(xué)名索引,因?yàn)檫@是讀者查找信息的兩種最常用的方法。
兩者的區(qū)別此處有個(gè)很清晰的例子:請(qǐng)點(diǎn)此處
選擇使用的場(chǎng)景:
這里寫圖片描述
關(guān)于聚集索引以及非聚集索引的幾個(gè)問題:
一)聚集索引的約束是唯一性,是否要求字段也是唯一的呢?
一般我們指定一個(gè)表的主鍵,如果這個(gè)表之前沒有聚集索引,同時(shí)建立主鍵時(shí)候沒有強(qiáng)制指定使用非聚集索引,SQL會(huì)默認(rèn)在此字段上創(chuàng)建一個(gè)聚集索引,而主鍵都是唯一的,所以理所當(dāng)然的認(rèn)為創(chuàng)建聚集索引的字段也需要唯一。
聚集索引可以創(chuàng)建在任何一列你想創(chuàng)建的字段上,這是從理論上講,實(shí)際情況并不能隨便指定,否則在性能上會(huì)是惡夢(mèng)。
二)|主鍵就是聚集索引???
這樣有時(shí)會(huì)對(duì)聚集索引的一種浪費(fèi)。Innodb將通過主鍵聚集數(shù)據(jù),如果沒有定義主鍵,Innodb會(huì)選擇第一個(gè)非空的唯一索引代替,如果沒有非空唯一索引,Innodb會(huì)隱式定義一個(gè)6字節(jié)的rowid主鍵來作為聚集索引。innodb只聚集在同一個(gè)頁面中的記錄,包含相鄰鍵值的頁面可能會(huì)相距甚遠(yuǎn)。
因?yàn)槊總€(gè)表中只能有一個(gè)聚集索引的規(guī)則,這使得聚集索引變得更加珍貴。
使用聚集索引的最大好處就是能夠根據(jù)查詢要求,迅速縮小查詢范圍,避免全表掃描。在實(shí)際應(yīng)用中,因?yàn)?ID號(hào)是自動(dòng)生成的,我們并不知道每條記錄的ID號(hào),所以我們很難在實(shí)踐中用ID號(hào)來進(jìn)行查詢。這就使讓ID號(hào)這個(gè)主鍵作為聚集索引成為一種資源浪費(fèi)。其次,讓每個(gè)ID號(hào)都不同的字段作為聚集索引也不符合“大數(shù)目的不同值情況下不應(yīng)建立聚合索引”規(guī)則;當(dāng)然,這種情況只是針對(duì)用戶經(jīng)常修改記錄內(nèi)容,特別是索引項(xiàng)的時(shí)候會(huì)負(fù)作用,但對(duì)于查詢速度并沒有影響。
三)是不是聚集索引就一定要比非聚集索引性能優(yōu)呢???
如果想查詢學(xué)分在60-90之間的學(xué)生的學(xué)分以及姓名,在學(xué)分上創(chuàng)建聚集索引是否是最優(yōu)的呢?
答:否。既然只輸出兩列,我們可以在學(xué)分以及學(xué)生姓名上創(chuàng)建聯(lián)合非聚集索引(也就是多列索引),此時(shí)的索引就形成了覆蓋索引,即索引所存儲(chǔ)的內(nèi)容就是最終輸出的數(shù)據(jù),這種索引在比以學(xué)分為聚集索引做查詢性能更好。
四)在數(shù)據(jù)庫中通過什么描述聚集索引與非聚集索引的?
索引是通過二叉樹的形式進(jìn)行描述的,我們可以這樣區(qū)分聚集與非聚集索引的區(qū)別:聚集索引的葉節(jié)點(diǎn)就是最終的數(shù)據(jù)節(jié)點(diǎn),而非聚集索引的葉節(jié)仍然是索引節(jié)點(diǎn),但它有一個(gè)指向最終數(shù)據(jù)的指針。
五)在主鍵是創(chuàng)建聚集索引的表在數(shù)據(jù)插入上為什么比主鍵上創(chuàng)建非聚集索引表速度要慢?
在有主鍵的表中插入數(shù)據(jù)行,由于有主鍵唯一性的約束,所以需要保證插入的數(shù)據(jù)沒有重復(fù)。我們來比較下主鍵為聚集索引和非聚集索引的查找情況:聚集索引由于索引葉節(jié)點(diǎn)就是數(shù)據(jù)頁,所以如果想檢查主鍵的唯一性,需要遍歷所有數(shù)據(jù)節(jié)點(diǎn)才行,但非聚集索引不同,由于非聚集索引上已經(jīng)包含了主鍵值,所以查找主鍵唯一性,只需要遍歷所有的索引頁就行,這比遍歷所有數(shù)據(jù)行減少了不少IO消耗。這就是為什么主鍵上創(chuàng)建非聚集索引比主鍵上創(chuàng)建聚集索引在插入數(shù)據(jù)時(shí)要快的真正原因。
四、索引設(shè)計(jì)優(yōu)化:
(1)索引建立的幾大原則:
1)最左前綴匹配原則,非常重要的原則,mysql會(huì)一直向右匹配直到遇到范圍查詢(>、<、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的順序可以任意調(diào)整。
2)=和in可以亂序,比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意順序,mysql的查詢優(yōu)化器會(huì)幫你優(yōu)化成索引可以識(shí)別的形式。
3)盡量選擇區(qū)分度高的列作為索引,區(qū)分度的公式是count(distinct col)/count(*),表示字段不重復(fù)的比例,比例越大我們掃描的記錄數(shù)越少,唯一鍵的區(qū)分度是1,而一些狀態(tài)、性別字段可能在大數(shù)據(jù)面前區(qū)分度就是0,那可能有人會(huì)問,這個(gè)比例有什么經(jīng)驗(yàn)值嗎?使用場(chǎng)景不同,這個(gè)值也很難確定,一般需要join的字段我們都要求是0.1以上,即平均1條掃描10條記錄
4)索引列不能參與計(jì)算,保持列“干凈”,比如from_unixtime(create_time) = ’2014-05-29’就不能使用到索引,原因很簡(jiǎn)單,b+樹中存的都是數(shù)據(jù)表中的字段值,但進(jìn)行檢索時(shí),需要把所有元素都應(yīng)用函數(shù)才能比較,顯然成本太大。所以語句應(yīng)該寫成create_time = unix_timestamp(’2014-05-29’);
5)盡量的擴(kuò)展索引,不要新建索引。比如表中已經(jīng)有a的索引,現(xiàn)在要加(a,b)的索引,那么只需要修改原來的索引即可。
6)定義有外鍵的數(shù)據(jù)列一定要建立索引。
7)對(duì)于那些查詢中很少涉及的列,重復(fù)值比較多的列不要建立索引。
8)對(duì)于定義為text、image和bit的數(shù)據(jù)類型的列不要建立索引。
9)對(duì)于經(jīng)常存取的列避免建立索引
(2)索引使用的注意點(diǎn):
一、)一般說來,索引應(yīng)建立在那些將用于JOIN,WHERE判斷和ORDER BY排序的字段上。盡量不要對(duì)數(shù)據(jù)庫中某個(gè)含有大量重復(fù)的值的字段建立索引。對(duì)于一個(gè)ENUM類型的字段來說,出現(xiàn)大量重復(fù)值是很有可能的情況。
二、)應(yīng)盡量避免在 where 子句中對(duì)字段進(jìn)行 null 值判斷,否則將導(dǎo)致引擎放棄使用索引而進(jìn)行全表掃描。如:
select idfromt where num isnull
最好不要給數(shù)據(jù)庫留NULL,盡可能的使用 NOT NULL填充數(shù)據(jù)庫.
備注、描述、評(píng)論之類的可以設(shè)置為 NULL,其他的,最好不要使用NULL。
不要以為 NULL 不需要空間,比如:char(100) 型,在字段建立時(shí),空間就固定了, 不管是否插入值(NULL也包含在內(nèi)),都是占用 100個(gè)字符的空間的,如果是varchar這樣的變長字段, null 不占用空間。
可以在num上設(shè)置默認(rèn)值0,確保表中num列沒有null值,然后這樣查詢:
三、)應(yīng)盡量避免在 where 子句中使用 != 或 <> 操作符,否則將引擎放棄使用索引而進(jìn)行全表掃描。
四、)應(yīng)盡量避免在 where 子句中使用 or 來連接條件,如果一個(gè)字段有索引,一個(gè)字段沒有索引,將導(dǎo)致引擎放棄使用索引而進(jìn)行全表掃描。如:
select idfromt where num=10orName ='fuzhu'
可以這樣查詢,充分利用索引:
select id from twherenum = 10union allselect id from twhereName ='fuzhu'
五、)in 和 not in 也要慎用,否則會(huì)導(dǎo)致全表掃描。
select id from twherenumin(1,2,3)
對(duì)于連續(xù)的數(shù)值,能用 between 就不要用 in 了:
select idfromt where num between1and3
很多時(shí)候用 exists 代替 in 是一個(gè)好的選擇
select numfroma where numin(select numfromb)
正上面的,用下面的語句替換:
select num from awhereexists(select 1 from bwherenum=a.num)
六、)下面的模糊查詢也將導(dǎo)致全表掃描:
select id from twherename like ‘%abc%’
一般情況下不鼓勵(lì)使用like操作,如果非使用不可,如何使用也是一個(gè)問題。like “%aaa%” 不會(huì)使用索引,而like “aaa%”可以使用索引。
若要提高效率,可以考慮全文檢索。
既然談到模糊查詢下使用索引,我們就順便詳細(xì)地講講吧。
1.? like %keyword? ? 索引失效,使用全表掃描。但可以通過翻轉(zhuǎn)函數(shù)+like前模糊查詢+建立翻轉(zhuǎn)函數(shù)索引=走翻轉(zhuǎn)函數(shù)索引,不走全表掃描。例子在此處
2.? like keyword%? ? 索引有效。
3.? like %keyword% 索引失效,也無法使用反向索引。
//可以拿我給出的數(shù)據(jù)庫試一下嘛。然后用explain測(cè)試,就能測(cè)出有沒有走索引了select *fromtable where code like'Classify_Description%'select *fromtable where code like'%Classify_Description%'select *fromtable where code like'%Classify_Description'
七、)如果在 where 子句中使用參數(shù),也會(huì)導(dǎo)致全表掃描。因?yàn)镾QL只有在運(yùn)行時(shí)才會(huì)解析局部變量,但優(yōu)化程序不能將訪問計(jì)劃的選擇推遲到運(yùn)行時(shí);它必須在編譯時(shí)進(jìn)行選擇。然 而,如果在編譯時(shí)建立訪問計(jì)劃,變量的值還是未知的,因而無法作為索引選擇的輸入項(xiàng)。如下面語句將進(jìn)行全表掃描:
select id from twherenum = @num
可以改為強(qiáng)制查詢使用索引:
select id from twith(index(索引名)) where num=@num
應(yīng)盡量避免在 where 子句中對(duì)字段進(jìn)行表達(dá)式操作,這將導(dǎo)致引擎放棄使用索引而進(jìn)行全表掃描。如:
select id from twherenum/2 = 100
正上面的應(yīng)改為:
select id from twherenum = 100*2
八、)應(yīng)盡量避免在where子句中對(duì)字段進(jìn)行函數(shù)操作,這將導(dǎo)致引擎放棄使用索引而進(jìn)行全表掃描。如:
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
應(yīng)改為:
select idfromt where name like'abc%'select idfromt where createdate >='2005-11-30'andcreatedate <'2005-12-1'
九、).不要在 where 子句中的“=”左邊進(jìn)行函數(shù)、算術(shù)運(yùn)算或其他表達(dá)式運(yùn)算,否則系統(tǒng)將可能無法正確使用索引。
十、)在使用索引字段作為條件時(shí),如果該索引是復(fù)合索引(多列索引),那么必須使用到該索引中的第一個(gè)字段作為條件時(shí)才能保證系統(tǒng)使用該索引,否則該索引將不會(huì)被使用,并且應(yīng)盡可能的讓字段順序與索引順序相一致。
十一、)索引并不是越多越好,索引固然可以提高相應(yīng)的 select 的效率,但同時(shí)也降低了 insert 及 update 的效率,因?yàn)?insert 或 update 時(shí)有可能會(huì)重建索引,所以怎樣建索引需要慎重考慮,視具體情況而定。一個(gè)表的索引數(shù)最好不要超過6個(gè),若太多則應(yīng)考慮一些不常使用到的列上建的索引是否有必要。
十二、)應(yīng)盡可能的避免更新 clustered 索引數(shù)據(jù)列,因?yàn)?clustered 索引數(shù)據(jù)列的順序就是表記錄的物理存儲(chǔ)順序,一旦該列值改變將導(dǎo)致整個(gè)表記錄的順序的調(diào)整,會(huì)耗費(fèi)相當(dāng)大的資源。若應(yīng)用系統(tǒng)需要頻繁更新 clustered 索引數(shù)據(jù)列,那么需要考慮是否應(yīng)將該索引建為 clustered 索引。
十三、)盡量避免向客戶端返回大數(shù)據(jù)量,若數(shù)據(jù)量過大,應(yīng)該考慮相應(yīng)需求是否合理。
十四、)MySQL查詢只使用一個(gè)索引,因此如果where子句中已經(jīng)使用了索引的話,那么order by中的列是不會(huì)使用索引的。因此數(shù)據(jù)庫默認(rèn)排序可以符合要求的情況下不要使用排序操作;盡量不要包含多個(gè)列的排序,如果需要最好給這些列創(chuàng)建復(fù)合索引。
好了,MySQL優(yōu)化筆記(三)--索引的使用、原理和設(shè)計(jì)優(yōu)化,這是積累的必經(jīng)一步,我會(huì)繼續(xù)出這個(gè)系列文章,分享經(jīng)驗(yàn)給大家。歡迎在下面指出錯(cuò)誤,共同學(xué)習(xí)!!你的點(diǎn)贊是對(duì)我最好的支持!!
作者:JackFrost_fuzhu
鏈接:http://www.lxweimin.com/p/6b080a787b61
來源:簡(jiǎn)書
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請(qǐng)注明出處。