之前的文章一直在規(guī)避索引的建立去優(yōu)化數(shù)據(jù)庫,不是不想講痪枫,而是這個太重要织堂,必須抽出來講叠艳。今天我們就來研究下數(shù)據(jù)庫索引的設(shè)計(jì)與優(yōu)化(MySQL為例)奶陈。
文章結(jié)構(gòu):(1)索引的概述和使用;(2)索引的基本原理附较;(3)索引分類吃粒;(4)索引設(shè)計(jì)優(yōu)化
文章目錄:
(1)索引的概述和使用
- 概述
- 什么是索引
- 索引的優(yōu)點(diǎn)
- 索引的缺點(diǎn)
- 為什么需要索引
- 索引的使用(語法)
- 創(chuàng)建索引:(三種方式)
- 刪除索引
- 查看索引
(2)索引的基本原理
- 整體性原理例子
- 針對存儲性質(zhì)講解
- 索引的數(shù)據(jù)結(jié)構(gòu):B+tree
- B+tree性質(zhì)
- B+tree有兩種搜索方法
(3)索引分類
- 普通索引
- 唯一索引
- 主鍵索引
- 全文索引:(FULLTEXT)
- 單列索引與多列索引(其實(shí)是相當(dāng)于一個用法技巧)(重點(diǎn))
- 單列索引
- 多列索引
- 聚集索引和非聚集索引
- 聚集索引
- 非聚集索引
- 關(guān)于聚集索引以及非聚集索引的幾個問題
(4)索引設(shè)計(jì)優(yōu)化:
- 索引建立的幾大原則
- 索引使用的注意點(diǎn)(大概有14點(diǎn))
一僻肖、索引的概述和使用:
(1)概述:
1)什么是索引肖爵?
索引是一種特殊的文件(InnoDB數(shù)據(jù)表上的索引是表空間的一個組成部分),它們包含著對數(shù)據(jù)表里所有記錄的引用指針臀脏。更通俗的說劝堪,數(shù)據(jù)庫索引好比是一本書前面的目錄,能加快數(shù)據(jù)庫的查詢速度揉稚。在沒有索引的情況下秒啦,數(shù)據(jù)庫會遍歷全部數(shù)據(jù)后選擇符合條件的;而有了相應(yīng)的索引之后搀玖,數(shù)據(jù)庫會直接在索引中查找符合條件的選項(xiàng)余境。
索引是一種特殊的文件(InnoDB數(shù)據(jù)表上的索引是表空間的一個組成部分),它們包含著對數(shù)據(jù)表里所有記錄的引用指針灌诅。更通俗的說芳来,數(shù)據(jù)庫索引好比是一本書前面的目錄,能加快數(shù)據(jù)庫的查詢速度延塑。在沒有索引的情況下绣张,數(shù)據(jù)庫會遍歷全部數(shù)據(jù)后選擇符合條件的;而有了相應(yīng)的索引之后关带,數(shù)據(jù)庫會直接在索引中查找符合條件的選項(xiàng)侥涵。
索引的性質(zhì)分類:
索引分為聚簇索引和非聚簇索引兩種,聚簇索引是按照數(shù)據(jù)存放的物理位置為順序的宋雏,而非聚簇索引就不一樣了芜飘;聚簇索引能提高多行檢索的速度,而非聚簇索引對于單行的檢索很快磨总。
2)索引的優(yōu)點(diǎn):
一】通過創(chuàng)建唯一性索引嗦明,可以保證數(shù)據(jù)庫表中每一行數(shù)據(jù)的唯一性。
二】可以大大加快數(shù)據(jù)的檢索速度蚪燕,這也是創(chuàng)建索引的最主要的原因娶牌。
三】可以加速表和表之間的連接,特別是在實(shí)現(xiàn)數(shù)據(jù)的參考完整性方面特別有意義馆纳。
四】在使用分組和排序 子句進(jìn)行數(shù)據(jù)檢索時诗良,同樣可以顯著減少查詢中分組和排序的時間。
五】通過使用索引鲁驶,可以在查詢的過程中鉴裹,使用優(yōu)化隱藏器,提高系統(tǒng)的性能。
3)索引的缺點(diǎn):
一】創(chuàng)建索引和維護(hù)索引要耗費(fèi)時間径荔,這種時間隨著數(shù)據(jù)量的增加而增加督禽。
二】索引需要占物理空間,除了數(shù)據(jù)表占數(shù)據(jù)空間之外总处,每一個索引還要占一定的物理空間狈惫,如果要建立聚簇索引,那么需要的空間就會更大鹦马。
三】當(dāng)對表中的數(shù)據(jù)進(jìn)行增加虱岂、刪除和修改的時候,索引也要動態(tài)的維護(hù)菠红,這樣就降低了數(shù)據(jù)的維護(hù)速度第岖。
4)為什么需要索引:
數(shù)據(jù)在磁盤上是以塊的形式存儲的。為確保對磁盤操作的原子性试溯,訪問數(shù)據(jù)的時候會一并訪問所有數(shù)據(jù)塊蔑滓。磁盤上的這些數(shù)據(jù)塊與鏈表類似,即它們都包含一個數(shù)據(jù)段和一個指針遇绞,指針指向下一個節(jié)點(diǎn)(數(shù)據(jù)塊)的內(nèi)存地址键袱,而且它們都不需要連續(xù)存儲(即邏輯上相鄰的數(shù)據(jù)塊在物理上可以相隔很遠(yuǎn))。
鑒于很多記錄只能做到按一個字段排序摹闽,所以要查詢某個未經(jīng)排序的字段蹄咖,就需要使用線性查找,即要訪問N/2個數(shù)據(jù)塊付鹿,其中N指的是一個表所涵蓋的所有數(shù)據(jù)塊澜汤。如果該字段是非鍵字段(也就是說,不包含唯一值)舵匾,那么就要搜索整個表空間俊抵,即要訪問全部N個數(shù)據(jù)塊。(在某些情況下坐梯,索引可以避免排序操作徽诲。)
然而,對于經(jīng)過排序的字段吵血,可以使用二分查找谎替,因此只要訪問log2 N個數(shù)據(jù)塊。同樣蹋辅,對于已經(jīng)排過序的非鍵字段钱贯,只要找到更大的值,也就不用再搜索表中的其他數(shù)據(jù)塊了晕翠。這樣一來喷舀,性能就會有實(shí)質(zhì)性的提升。
一】創(chuàng)建索引:(三種方式)
第一種方式:
//第一種方式:
//在執(zhí)行CREATE TABLE 時創(chuàng)建索引:(硬設(shè)一個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用來創(chuàng)建普通索引淋肾、UNIQUE索引或PRIMARY KEY索引硫麻。
//標(biāo)準(zhǔn)語句:
ALTER TABLE table_name ADD INDEX index_name (column_list)//添加普通索引,索引值可出現(xiàn)多次樊卓。
ALTER TABLE table_name ADD UNIQUE (column_list)//這條語句創(chuàng)建的索引的值必須是唯一的(除了NULL外拿愧,NULL可能會出現(xiàn)多次)。
ALTER TABLE table_name ADD PRIMARY KEY (column_list)//該語句添加一個主鍵碌尔,這意味著索引值必須是唯一的浇辜,且不能為NULL。
ALTER TABLE table_name ADD FULLTEXT index_name(olumu_name);該語句指定了索引為FULLTEXT唾戚,用于全文索引柳洋。
//針對上述數(shù)據(jù)庫,增加商品分類的索引
ALTER table commodity_list ADD INDEX classify_index (Classify_Description)
其中table_name是要增加索引的表名叹坦,column_list指出對哪些列進(jìn)行索引熊镣,多列時各列之間用逗號分隔。索引名index_name可自己命名募书,缺省時绪囱,MySQL將根據(jù)第一個索引列賦一個名稱。另外莹捡,ALTER TABLE允許在單個語句中更改多個表鬼吵,因此可以在同時創(chuàng)建多個索引。
第三種方式:使用CREATE INDEX命令創(chuàng)建
CREATE INDEX可對表增加普通索引或UNIQUE索引篮赢。
//標(biāo)準(zhǔn)語句:
CREATE INDEX index_name ON table_name (column_list)
CREATE UNIQUE INDEX index_name ON table_name (column_list)
//針對上述數(shù)據(jù)庫:
CREATE INDEX classify_index ON commodity_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 ;
//針對上述數(shù)據(jù)庫
drop index classify_index on commodity_list ;
其中,在前面的兩條語句中坠敷,都刪除了table_name中的索引index_name妙同。而在最后一條語句中,只在刪除PRIMARY KEY索引中使用膝迎,因?yàn)橐粋€表只可能有一個PRIMARY KEY索引粥帚,因此不需要指定索引名。如果沒有創(chuàng)建PRIMARY KEY索引限次,但表具有一個或多個UNIQUE索引芒涡,則MySQL將刪除第一個UNIQUE索引柴灯。
如果從表中刪除某列,則索引會受影響费尽。對于多列組合的索引赠群,如果刪除其中的某列,則該列也會從索引中刪除旱幼。如果刪除組成索引的所有列查描,則整個索引將被刪除。
三】查看索引:
SHOW INDEX FROM [table_name];
show keys from [table_name];
Table:表的名稱柏卤。
Non_unique:如果索引不能包括重復(fù)詞冬三,則為0。如果可以缘缚,則為1勾笆。
Key_name:索引的名稱。
Seq_in_index:索引中的列序列號桥滨,從1開始匠襟。
Column_name:列名稱。
Collation:列以什么方式存儲在索引中该园。在MySQL中酸舍,有值‘A'(升序)或NULL(無分類)。
Cardinality:索引中唯一值的數(shù)目的估計(jì)值里初。通過運(yùn)行ANALYZE TABLE或myisamchk -a可以更新啃勉。基數(shù)根據(jù)被存儲為整數(shù)的統(tǒng)計(jì)數(shù)據(jù)來計(jì)數(shù)双妨,所以即使對于小型表淮阐,該值也沒有必要是精確的〉笃罚基數(shù)越大泣特,當(dāng)進(jìn)行聯(lián)合時,MySQL使用該索引的機(jī)會就越大挑随。
Sub_part:如果列只是被部分地編入索引状您,則為被編入索引的字符的數(shù)目。如果整列被編入索引兜挨,則為NULL膏孟。
Packed:指示關(guān)鍵字如何被壓縮。如果沒有被壓縮拌汇,則為NULL柒桑。
Null:如果列含有NULL,則含有YES噪舀。如果沒有魁淳,則該列含有NO飘诗。
Index_type:用過的索引方法(BTREE, FULLTEXT, HASH, RTREE)。
Comment:注釋
二界逛、索引的基本原理:
舉例解析基本原理:
整體性原理例子:
除了詞典昆稿,生活中隨處可見索引的例子,如火車站的車次表仇奶、圖書的目錄等。它們的原理都是一樣的比驻,通過不斷的縮小想要獲得數(shù)據(jù)的范圍來篩選出最終想要的結(jié)果该溯,同時把隨機(jī)的事件變成順序的事件,也就是我們總是通過同一種查找方式來鎖定數(shù)據(jù)别惦。
SQL的應(yīng)用場景會使用索引:
數(shù)據(jù)庫也是一樣狈茉,但顯然要復(fù)雜許多,因?yàn)椴粌H面臨著等值查詢掸掸,還有范圍查詢(>氯庆、<、between扰付、in)堤撵、模糊查詢(like)、并集查詢(or)等等羽莺。數(shù)據(jù)庫應(yīng)該選擇怎么樣的方式來應(yīng)對所有的問題呢实昨?我們回想字典的例子,能不能把數(shù)據(jù)分成段盐固,然后分段查詢呢荒给?最簡單的如果1000條數(shù)據(jù),1到100分成第一段刁卜,101到200分成第二段志电,201到300分成第三段......這樣查第250條數(shù)據(jù),只要找第三段就可以了蛔趴,一下子去除了90%的無效數(shù)據(jù)挑辆。
由于存儲介質(zhì)的特性,磁盤本身存取就比主存慢很多孝情,再加上機(jī)械運(yùn)動耗費(fèi)之拨,磁盤的存取速度往往是主存的幾百分分之一,因此為了提高效率咧叭,要盡量減少磁盤I/O蚀乔。為了達(dá)到這個目的,磁盤往往不是嚴(yán)格按需讀取菲茬,而是每次都會預(yù)讀吉挣,即使只需要一個字節(jié)派撕,磁盤也會從這個位置開始,順序向后讀取一定長度的數(shù)據(jù)放入內(nèi)存睬魂。這樣做的理論依據(jù)是計(jì)算機(jī)科學(xué)中著名的局部性原理:當(dāng)一個數(shù)據(jù)被用到時终吼,其附近的數(shù)據(jù)也通常會馬上被使用。程序運(yùn)行期間所需要的數(shù)據(jù)通常比較集中氯哮。
由于磁盤順序讀取的效率很高(不需要尋道時間际跪,只需很少的旋轉(zhuǎn)時間),因此對于具有局部性的程序來說喉钢,預(yù)讀可以提高I/O效率姆打。
預(yù)讀的長度一般為頁(page)的整倍數(shù)。頁是計(jì)算機(jī)管理存儲器的邏輯塊肠虽,硬件及操作系統(tǒng)往往將主存和磁盤存儲區(qū)分割為連續(xù)的大小相等的塊幔戏,每個存儲塊稱為一頁(在許多操作系統(tǒng)中,頁得大小通常為4k)税课,主存和磁盤以頁為單位交換數(shù)據(jù)闲延。當(dāng)程序要讀取的數(shù)據(jù)不在主存中時,會觸發(fā)一個缺頁異常韩玩,此時系統(tǒng)會向磁盤發(fā)出讀盤信號垒玲,磁盤會找到數(shù)據(jù)的起始位置并向后連續(xù)讀取一頁或幾頁載入內(nèi)存中,然后異常返回找颓,程序繼續(xù)運(yùn)行侍匙。
B樹中每個節(jié)點(diǎn)包含了鍵值和鍵值對于的數(shù)據(jù)對象存放地址指針叮雳,所以成功搜索一個對象可以不用到達(dá)樹的葉節(jié)點(diǎn)想暗。
成功搜索包括節(jié)點(diǎn)內(nèi)搜索和沿某一路徑的搜索,成功搜索時間取決于關(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)鍵字在某個Ki或Ki+1之間捣郊,于是取Pi所指的下一層索引節(jié)點(diǎn)塊繼續(xù)查找辽狈,直到找到,或指針Pi為空時查找失敗呛牲。
B+tree性質(zhì):
1.)n棵子tree的節(jié)點(diǎn)包含n個關(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ù)對象的地址指針,非葉節(jié)點(diǎn)只是索引部分敬特。所有的葉節(jié)點(diǎn)在同一層上掰邢,包含了全部關(guān)鍵碼和相應(yīng)數(shù)據(jù)對象的存放地址指針,且葉節(jié)點(diǎn)按關(guān)鍵碼從小到大順序鏈接伟阔。如果實(shí)際數(shù)據(jù)對象按加入的順序存儲而不是按關(guān)鍵碼次數(shù)存儲的話辣之,葉節(jié)點(diǎn)的索引必須是稠密索引,若實(shí)際數(shù)據(jù)存儲按關(guān)鍵碼次序存放的話减俏,葉節(jié)點(diǎn)索引時稀疏索引召烂。
B+ 樹中碱工,數(shù)據(jù)對象的插入和刪除僅在葉節(jié)點(diǎn)上進(jìn)行娃承。
B+樹有2個頭指針,一個是樹的根節(jié)點(diǎn)怕篷,一個是最小關(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樹中同一鍵值不會出現(xiàn)多次,并且它有可能出現(xiàn)在葉結(jié)點(diǎn)榛鼎,也有可能出現(xiàn)在非葉結(jié)點(diǎn)中逃呼。而B+樹的鍵一定會出現(xiàn)在葉結(jié)點(diǎn)中,并且有可能在非葉結(jié)點(diǎn)中也有可能重復(fù)出現(xiàn)者娱,以維持B+樹的平衡抡笼。
2)因?yàn)锽樹鍵位置不定,且在整個樹結(jié)構(gòu)中只出現(xiàn)一次黄鳍,雖然可以節(jié)省存儲空間推姻,但使得在插入、刪除操作復(fù)雜度明顯增加框沟。B+樹相比來說是一種較好的折中拾碌。
3)B樹的查詢效率與鍵在樹中的位置有關(guān)吐葱,最大時間復(fù)雜度與B+樹相同(在葉結(jié)點(diǎn)的時候),最小時間復(fù)雜度為1(在根結(jié)點(diǎn)的時候)校翔。而B+樹的時候復(fù)雜度對某建成的樹是固定的弟跑。
上圖展示了一種可能的索引方式。左邊是數(shù)據(jù)表防症,一共有兩列七條記錄孟辑,最左邊的是數(shù)據(jù)記錄的物理地址(注意邏輯上相鄰的記錄在磁盤上也并不是一定物理相鄰的)。為了加快Col2的查找蔫敲,可以維護(hù)一個右邊所示的二叉查找樹饲嗽,每個節(jié)點(diǎn)分別包含索引鍵值和一個指向?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 INDEX index_name (column_list)
CREATE INDEX index_name ON table_name (column_list);
//還有建表的時候創(chuàng)建亦可
CREATE TABLE table_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掺冠,也就是索引這個字段的記錄的前10個字符沉馆。
二)唯一索引:
與前面的普通索引類似,不同的就是:MySQL數(shù)據(jù)庫索引列的值必須唯一德崭,但允許有空值斥黑。如果是組合索引,則列值的組合必須唯一眉厨。
它有以下幾種創(chuàng)建方式:
ALTER TABLE table_name ADD UNIQUE (column_list)
CREATE UNIQUE INDEX index_name ON table_name (column_list)
//還有建表時創(chuàng)建
CREATE TABLE table_name (
ID INT NOT NULL,
column_list VARCHAR(16) NOT NULL,
UNIQUE [index_name ]
(column_list(length))
);
三)主鍵索引:
它是一種特殊的唯一索引锌奴,不允許有空值。一般是在建表的時候同時創(chuàng)建主鍵索引:
CREATE TABLE table_name (
ID INT NOT NULL,
[column] VARCHAR(16) NOT NULL,
PRIMARY KEY(ID)
);
四)全文索引:(FULLTEXT)
定義:
全文檢索是對大數(shù)據(jù)文本進(jìn)行索引缺猛,在建立的索引中對要查找的單詞進(jìn)行進(jìn)行搜索缨叫,定位哪些文本數(shù)據(jù)包括要搜索的單詞。因此荔燎,全文檢索的全部工作就是建立索引和在索引中搜索定位耻姥,所有的工作都是圍繞這兩個來進(jìn)行的。
此索引關(guān)鍵:
建立全文索引中有兩項(xiàng)非常重要有咨,一個是如何對文本進(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被添加蘑秽。
但是要注意:對于較大的數(shù)據(jù)集饺著,將你的資料輸入一個沒有FULLTEXT索引的表中,然后創(chuàng)建索引肠牲,其速度比把資料輸入現(xiàn)有FULLTEXT索引的速度更為快幼衰。不過切記對于大容量的數(shù)據(jù)表,生成全文索引是一個非常消耗時間非常消耗硬盤空間的做法缀雳。因?yàn)椋渡嚣。〔迦胄薷膭h除表的同時也要針對索引做一系列的處理肥印。
創(chuàng)建方法:
//針對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的數(shù)據(jù)庫引擎的數(shù)據(jù)表
如果是其他數(shù)據(jù)引擎识椰,則全文索引不會生效。
SELECT * FROM article WHERE MATCH( content) AGAINST('想查詢的字符串')
此外竖独,MySQL自帶的全文索引只能對英文進(jìn)行全文檢索裤唠,目前無法對中文進(jìn)行全文檢索挤牛。如果需要對包含中文在內(nèi)的文本數(shù)據(jù)進(jìn)行全文檢索莹痢,我們需要采用Sphinx(斯芬克斯)/Coreseek技術(shù)來處理中文。
注意:
目前墓赴,使用MySQL自帶的全文索引時竞膳,如果查詢字符串的長度過短將無法得到期望的搜索結(jié)果。MySQL全文索引所能找到的詞的默認(rèn)最小長度為4個字符诫硕。另外坦辟,如果查詢的字符串包含停止詞,那么該停止詞將會被忽略章办。
如果可能锉走,請盡量先創(chuàng)建表并插入所有數(shù)據(jù)后再創(chuàng)建全文索引,而不要在創(chuàng)建表時就直接創(chuàng)建全文索引藕届,因?yàn)榍罢弑群笳叩娜乃饕室摺?/h4>
五)單列索引與多列索引(其實(shí)是相當(dāng)于一個用法技巧)
單列索引挪蹭,就是平常的只索引一個一個的字段的方式
//例子為name列的頭10個字符創(chuàng)建一個索引:
CREATE TABLE test (
name CHAR(200) NOT NULL,
KEY index_name (name(10))
);
多列索引(也叫組合索引):
相關(guān)概念(適用多列索引的原因):
MySQL能在多個列上創(chuàng)建索引。一個索引可以由最多15個列組成休偶。(在CHAR和VARCHAR列上梁厉,你也可以使用列的前綴作為一個索引的部分)。
一個多重列索引可以認(rèn)為是包含通過合并(concatenate)索引列值創(chuàng)建的值的一個排序數(shù)組踏兜。
多個單列索引與單個多列索引的查詢效果不同词顾,因?yàn)閳?zhí)行查詢時八秃,MySQL只能使用一個索引,會從多個單列索引中選擇一個限制最為嚴(yán)格(獲得結(jié)果集記錄數(shù)最少)的索引肉盹。
當(dāng)你為在一個WHERE子句索引的第一列指定已知的數(shù)量時昔驱,MySQL以這種方式使用多重列索引使得查詢非常快速上忍,即使你不為其他列指定值舍悯。
適用場景:
1.全字段匹配
2.匹配部分最左前綴
3.匹配第一列
4.匹配第一列范圍查詢(可用用like a%,但不能使用like %b)
5.精確匹配某一列和和范圍匹配另外一列
例子:
//假設(shè)只使用單列索引名字
ALTER TABLE people ADD INDEX name (name);
//使用多列索引:
ALTER TABLE people ADD INDEX height_name_age (height,name,age);
//相當(dāng)于創(chuàng)建了(height)單列索引,(height,name)組合索引以及(height,name,age)組合索引
/*
注意:
注:在mysql中執(zhí)行查詢時睡雇,只能使用一個索引萌衬,如果我們在name,age上分別建索引,執(zhí)行查詢時,只能使用一個索引它抱,mysql會選擇一個最嚴(yán)格(獲得結(jié)果集記錄數(shù)最少)的索引秕豫。
*/
注意:
在創(chuàng)建多列索引時雷逆,要根據(jù)業(yè)務(wù)需求敌完,where子句中使用最頻繁的一列放在最左邊乌庶。
組合索引(多列索引)的原則:
原則:
最左前綴:顧名思義杭朱,就是最左優(yōu)先
平時用的SQL查詢語句一般都有比較多的限制條件赖欣,所以為了進(jìn)一步榨取MySQL的效率瘫拣,就要考慮建立組合索引(多列索引)陕赃。例如上面使用的例子就相當(dāng)于創(chuàng)建了(height)單列索引省艳,(height,name)組合索引以及(height,name,age)組合索引亲茅。
此外回铛,補(bǔ)充一個概念對比,那就是聚集索引和非聚集索引:
定義:
該索引中鍵值的邏輯順序決定了表中相應(yīng)行的物理順序克锣。
聚集索引確定表中數(shù)據(jù)的物理順序茵肃。聚集索引類似于電話簿,后者按姓氏排列數(shù)據(jù)袭祟。由于聚集索引規(guī)定數(shù)據(jù)在表中的物理存儲順序验残,因此一個表只能包含一個聚集索引。但該索引可以包含多個列(組合索引)巾乳,就像電話簿按姓氏和名字進(jìn)行組織一樣您没。
注意事項(xiàng):
定義聚集索引鍵時使用的列越少越好。
使用的場景:
一)包含大量非重復(fù)值的列胆绊。
二)使用下列運(yùn)算符返回一個范圍值的查詢:BETWEEN氨鹏、>、>=辑舷、< 和 <=喻犁。
三)被連續(xù)訪問的列。
四)返回大型結(jié)果集的查詢。
五)經(jīng)常被使用聯(lián)接或 GROUP BY 子句的查詢訪問的列肢础;一般來說还栓,這些是外鍵列。對 ORDER BY 或 GROUP BY 子句中指定的列進(jìn)行索引传轰,可以使 SQL Server 不必對數(shù)據(jù)進(jìn)行排序剩盒,因?yàn)檫@些行已經(jīng)排序。這樣可以提高查詢性能慨蛙。
六)OLTP 類型的應(yīng)用程序辽聊,這些程序要求進(jìn)行非常快速的單行查找(一般通過主鍵)期贫。
不適用于:
頻繁更改的列 跟匆。這將導(dǎo)致整行移動(因?yàn)?SQL Server 必須按物理順序保留行中的數(shù)據(jù)值)。這一點(diǎn)要特別注意通砍,因?yàn)樵诖髷?shù)據(jù)量事務(wù)處理系統(tǒng)中數(shù)據(jù)是易失的玛臂。
寬鍵 。來自聚集索引的鍵值由所有非聚集索引作為查找鍵使用封孙,因此存儲在每個非聚集索引的葉條目內(nèi)迹冤。
2)非聚集索引:
定義:
數(shù)據(jù)存儲在一個地方,索引存儲在另一個地方虎忌,索引帶有指針指向數(shù)據(jù)的存儲位置泡徙。
非聚集索引中的項(xiàng)目按索引鍵值的順序存儲,而表中的信息按另一種順序存儲(這可以由聚集索引規(guī)定)膜蠢。對于非聚集索引堪藐,可以為在表非聚集索引中查找數(shù)據(jù)時常用的每個列創(chuàng)建一個非聚集索引。有些書籍包含多個索引狡蝶。例如庶橱,一本介紹園藝的書可能會包含一個植物通俗名稱索引贮勃,和一個植物學(xué)名索引贪惹,因?yàn)檫@是讀者查找信息的兩種最常用的方法。
選擇使用的場景:
關(guān)于聚集索引以及非聚集索引的幾個問題:
一)聚集索引的約束是唯一性寂嘉,是否要求字段也是唯一的呢奏瞬?
一般我們指定一個表的主鍵,如果這個表之前沒有聚集索引泉孩,同時建立主鍵時候沒有強(qiáng)制指定使用非聚集索引硼端,SQL會默認(rèn)在此字段上創(chuàng)建一個聚集索引,而主鍵都是唯一的寓搬,所以理所當(dāng)然的認(rèn)為創(chuàng)建聚集索引的字段也需要唯一珍昨。
聚集索引可以創(chuàng)建在任何一列你想創(chuàng)建的字段上,這是從理論上講,實(shí)際情況并不能隨便指定镣典,否則在性能上會是惡夢兔毙。
二)|主鍵就是聚集索引?兄春?澎剥?
這樣有時會對聚集索引的一種浪費(fèi)。Innodb將通過主鍵聚集數(shù)據(jù)赶舆,如果沒有定義主鍵哑姚,Innodb會選擇第一個非空的唯一索引代替,如果沒有非空唯一索引芜茵,Innodb會隱式定義一個6字節(jié)的rowid主鍵來作為聚集索引叙量。innodb只聚集在同一個頁面中的記錄,包含相鄰鍵值的頁面可能會相距甚遠(yuǎn)九串。
因?yàn)槊總€表中只能有一個聚集索引的規(guī)則宛乃,這使得聚集索引變得更加珍貴。
使用聚集索引的最大好處就是能夠根據(jù)查詢要求蒸辆,迅速縮小查詢范圍征炼,避免全表掃描。在實(shí)際應(yīng)用中躬贡,因?yàn)?ID號是自動生成的谆奥,我們并不知道每條記錄的ID號,所以我們很難在實(shí)踐中用ID號來進(jìn)行查詢拂玻。這就使讓ID號這個主鍵作為聚集索引成為一種資源浪費(fèi)酸些。其次,讓每個ID號都不同的字段作為聚集索引也不符合“大數(shù)目的不同值情況下不應(yīng)建立聚合索引”規(guī)則檐蚜;當(dāng)然魄懂,這種情況只是針對用戶經(jīng)常修改記錄內(nèi)容,特別是索引項(xiàng)的時候會負(fù)作用闯第,但對于查詢速度并沒有影響市栗。
三)是不是聚集索引就一定要比非聚集索引性能優(yōu)呢?咳短?填帽?
如果想查詢學(xué)分在60-90之間的學(xué)生的學(xué)分以及姓名,在學(xué)分上創(chuàng)建聚集索引是否是最優(yōu)的呢咙好?
答:否篡腌。既然只輸出兩列,我們可以在學(xué)分以及學(xué)生姓名上創(chuàng)建聯(lián)合非聚集索引(也就是多列索引)勾效,此時的索引就形成了覆蓋索引嘹悼,即索引所存儲的內(nèi)容就是最終輸出的數(shù)據(jù)叛甫,這種索引在比以學(xué)分為聚集索引做查詢性能更好。
四)在數(shù)據(jù)庫中通過什么描述聚集索引與非聚集索引的杨伙?
索引是通過二叉樹的形式進(jìn)行描述的合溺,我們可以這樣區(qū)分聚集與非聚集索引的區(qū)別:聚集索引的葉節(jié)點(diǎn)就是最終的數(shù)據(jù)節(jié)點(diǎn),而非聚集索引的葉節(jié)仍然是索引節(jié)點(diǎn)缀台,但它有一個指向最終數(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è)計(jì)優(yōu)化:
(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的順序可以任意調(diào)整整吆。
2)=和in可以亂序,比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意順序圈暗,mysql的查詢優(yōu)化器會幫你優(yōu)化成索引可以識別的形式掂为。
3)盡量選擇區(qū)分度高的列作為索引,區(qū)分度的公式是count(distinct col)/count(*),表示字段不重復(fù)的比例员串,比例越大我們掃描的記錄數(shù)越少,唯一鍵的區(qū)分度是1昼扛,而一些狀態(tài)寸齐、性別字段可能在大數(shù)據(jù)面前區(qū)分度就是0欲诺,那可能有人會問,這個比例有什么經(jīng)驗(yàn)值嗎渺鹦?使用場景不同扰法,這個值也很難確定,一般需要join的字段我們都要求是0.1以上毅厚,即平均1條掃描10條記錄
4)索引列不能參與計(jì)算塞颁,保持列“干凈”,比如from_unixtime(create_time) = ’2014-05-29’就不能使用到索引吸耿,原因很簡單祠锣,b+樹中存的都是數(shù)據(jù)表中的字段值,但進(jìn)行檢索時咽安,需要把所有元素都應(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)對于那些查詢中很少涉及的列红选,重復(fù)值比較多的列不要建立索引刺啦。
8)對于定義為text、image和bit的數(shù)據(jù)類型的列不要建立索引纠脾。
9)對于經(jīng)常存取的列避免建立索引
(2)索引使用的注意點(diǎn):
一玛瘸、)一般說來,索引應(yīng)建立在那些將用于JOIN,WHERE判斷和ORDER BY排序的字段上苟蹈。盡量不要對數(shù)據(jù)庫中某個含有大量重復(fù)的值的字段建立索引糊渊。對于一個ENUM類型的字段來說,出現(xiàn)大量重復(fù)值是很有可能的情況慧脱。
二渺绒、)應(yīng)盡量避免在 where 子句中對字段進(jìn)行 null 值判斷,否則將導(dǎo)致引擎放棄使用索引而進(jìn)行全表掃描菱鸥。如:
select id from t where num is null
最好不要給數(shù)據(jù)庫留NULL宗兼,盡可能的使用 NOT NULL填充數(shù)據(jù)庫.
備注、描述氮采、評論之類的可以設(shè)置為 NULL殷绍,其他的,最好不要使用NULL鹊漠。
不要以為 NULL 不需要空間主到,比如:char(100) 型茶行,在字段建立時,空間就固定了登钥, 不管是否插入值(NULL也包含在內(nèi))畔师,都是占用 100個字符的空間的,如果是varchar這樣的變長字段牧牢, null 不占用空間看锉。
可以在num上設(shè)置默認(rèn)值0,確保表中num列沒有null值塔鳍,然后這樣查詢:
三伯铣、)應(yīng)盡量避免在 where 子句中使用 != 或 <> 操作符,否則將引擎放棄使用索引而進(jìn)行全表掃描献幔。
四懂傀、)應(yīng)盡量避免在 where 子句中使用 or 來連接條件,如果一個字段有索引蜡感,一個字段沒有索引蹬蚁,將導(dǎo)致引擎放棄使用索引而進(jìn)行全表掃描。如:
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 也要慎用,否則會導(dǎo)致全表掃描情连。
select id from t where num in(1,2,3)
對于連續(xù)的數(shù)值叽粹,能用 between 就不要用 in 了:
select id from t where num between 1 and 3
很多時候用 exists 代替 in 是一個好的選擇
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)
六却舀、)下面的模糊查詢也將導(dǎo)致全表掃描:
select id from t where name like ‘%abc%’
一般情況下不鼓勵使用like操作虫几,如果非使用不可,如何使用也是一個問題挽拔。like “%aaa%” 不會使用索引辆脸,而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測試袍镀,就能測出有沒有走索引了
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 子句中使用參數(shù),也會導(dǎo)致全表掃描苇羡。因?yàn)镾QL只有在運(yùn)行時才會解析局部變量绸吸,但優(yōu)化程序不能將訪問計(jì)劃的選擇推遲到運(yùn)行時;它必須在編譯時進(jìn)行選擇设江。然 而锦茁,如果在編譯時建立訪問計(jì)劃,變量的值還是未知的叉存,因而無法作為索引選擇的輸入項(xiàng)码俩。如下面語句將進(jìn)行全表掃描:
select id from t where num = @num
可以改為強(qiáng)制查詢使用索引:
select id from t with(index(索引名)) where num = @num
應(yīng)盡量避免在 where 子句中對字段進(jìn)行表達(dá)式操作,這將導(dǎo)致引擎放棄使用索引而進(jìn)行全表掃描歼捏。如:
select id from t where num/2 = 100
正上面的應(yīng)改為:
select id from t where num = 100*2
八稿存、)應(yīng)盡量避免在where子句中對字段進(jìn)行函數(shù)操作,這將導(dǎo)致引擎放棄使用索引而進(jìn)行全表掃描瞳秽。如:
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
應(yīng)改為:
select id from t where name like 'abc%'
select id from t where createdate >= '2005-11-30' and createdate < '2005-12-1'
九瓣履、).不要在 where 子句中的“=”左邊進(jìn)行函數(shù)、算術(shù)運(yùn)算或其他表達(dá)式運(yùn)算练俐,否則系統(tǒng)將可能無法正確使用索引袖迎。
十、)在使用索引字段作為條件時腺晾,如果該索引是復(fù)合索引(多列索引)燕锥,那么必須使用到該索引中的第一個字段作為條件時才能保證系統(tǒng)使用該索引,否則該索引將不會被使用悯蝉,并且應(yīng)盡可能的讓字段順序與索引順序相一致归形。
十一、)索引并不是越多越好泉粉,索引固然可以提高相應(yīng)的 select 的效率连霉,但同時也降低了 insert 及 update 的效率,因?yàn)?insert 或 update 時有可能會重建索引嗡靡,所以怎樣建索引需要慎重考慮跺撼,視具體情況而定。一個表的索引數(shù)最好不要超過6個讨彼,若太多則應(yīng)考慮一些不常使用到的列上建的索引是否有必要歉井。
十二、)應(yīng)盡可能的避免更新 clustered 索引數(shù)據(jù)列哈误,因?yàn)?clustered 索引數(shù)據(jù)列的順序就是表記錄的物理存儲順序哩至,一旦該列值改變將導(dǎo)致整個表記錄的順序的調(diào)整躏嚎,會耗費(fèi)相當(dāng)大的資源。若應(yīng)用系統(tǒng)需要頻繁更新 clustered 索引數(shù)據(jù)列菩貌,那么需要考慮是否應(yīng)將該索引建為 clustered 索引卢佣。
十三、)盡量避免向客戶端返回大數(shù)據(jù)量箭阶,若數(shù)據(jù)量過大虚茶,應(yīng)該考慮相應(yīng)需求是否合理。
十四仇参、)MySQL查詢只使用一個索引嘹叫,因此如果where子句中已經(jīng)使用了索引的話,那么order by中的列是不會使用索引的诈乒。因此數(shù)據(jù)庫默認(rèn)排序可以符合要求的情況下不要使用排序操作罩扇;盡量不要包含多個列的排序,如果需要最好給這些列創(chuàng)建復(fù)合索引怕磨。
好了喂饥,MySQL優(yōu)化筆記(三)--索引的使用、原理和設(shè)計(jì)優(yōu)化癌压,這是積累的必經(jīng)一步仰泻,我會繼續(xù)出這個系列文章,分享經(jīng)驗(yàn)給大家滩届。歡迎在下面指出錯誤集侯,共同學(xué)習(xí)!帜消!你的點(diǎn)贊是對我最好的支持L耐鳌!