零.索引簡介
1. 索引是什么
①MySQL官方對索引的定義是:索引(Index)是幫助MySQL高效獲取數據的數據結構。
②可以簡單的理解為“排好序的快速查找數據結構”。
③除了數據本身之外,數據庫還維護著一個滿足特定查找算法的數據結構,這種數據結構以某種方式指向數據,這樣就可以在這些數據結構的基礎上實現高級查找算法,這種數據結構就是索引。
④一般來說索引本身也很大,不可能全部存儲在內存中,因此索引往往以索引文件的形式存儲在磁盤上。
⑤我們平常所說的索引,如果沒有特別說明,都是值B+樹結構組織的索引。其中聚集索引,稀疏索引、覆蓋索引、復合索引、前綴索引、唯一索引,默認都使用B+樹索引,統稱索引。當然除了B+樹之外還有哈希索引、BitMap索引等。
2.索引的優勢
①類似圖書的目錄索引,提高了數據檢索的效率,降低數據庫的IO成本。
②通過索引對數據進行排序,降低數據排序的成本,降低了CPU的消耗。
3.索引的劣勢
①實際上索引也是一張表,該表保存了主鍵與索引字段,并指向實體表的記錄,所以索引也是要占用空間的。
②雖然索引大大提高了查詢速度,同時卻會降低更新表的數據,如對表進行INSERT、UPDATE、DELETE。因為更新表時,MySQL不僅要保存數據,還要保存一下索引文件;每次更新添加了索引列的字段,都會調整因為更新所帶來的鍵值變化后的索引信息。
③索引只是提高效率的一個因素,如果你的MySQL有大數據量的表,就需要花費時間研究建立最優的索引。或者優化查詢語句。
4.索引的分類
①單值索引:一個索引包含單個列,一個表可以有多個單列索引。
②唯一索引:索引列的值必須唯一,但允許有空值。
③復合索引:一個索引包含多個列。
一.使用二叉查找樹作為索引存在的問題
二叉查找樹(Binary Search Tree),(又:二叉搜索樹,二叉排序樹)它或者是一棵空樹,或者是具有下列性質的二叉樹: 若它的左子樹不空,則左子樹上所有結點的值均小于它的根結點的值; 若它的右子樹不空,則右子樹上所有結點的值均大于它的根結點的值; 它的左、右子樹也分別為二叉排序樹。
如上面的圖所示,它存在的問題如下:
①如果數據太多而且大量數據單調,那么二叉搜索樹就會在一條分支上一直進行延伸,這樣基本就由二叉搜索樹變成了線性表,搜索效率就會大大下降。
②一個節點只能存儲一個值,數據量太大的時候就會一顆高度特別高的二叉樹,這樣搜索效率就會出現大幅下降。
③使用平衡二叉搜索樹,雖然可以防止編程線性表,但是每次更新索引都會樹的旋轉,這樣就會消耗特別大的性能,而且一個節點還是只能保存一個值,無法解決樹過高的問題。
二.使用B樹作為索引存在的問題
一棵m階B樹(balanced tree of order m)是一棵平衡的m路搜索樹。它或者是空樹,或者是滿足下列性質的樹:
1、根結點至少有兩個子女;
2、每個非根節點所包含的關鍵字個數 j 滿足:┌m/2┐ - 1 <= j <= m - 1;
3、除根結點以外的所有結點(不包括葉子結點)的度數正好是關鍵字總數加1,故內部子樹個數 k 滿足:┌m/2┐ <= k <= m ;
4、所有的葉子結點都位于同一層。
在B-樹中,每個結點中關鍵字從小到大排列,并且當該結點的孩子是非葉子結點時,該k-1個關鍵字正好是k個孩子包含的關鍵字的值域的分劃。
①相比二叉樹,他一個節點可以容納多個節點,可以降低樹的高度,增加查找效率。
②但是一層能容納的數量還是太少,數據量一大樹的高度也會變得很高,搜索效率一樣會進行大幅度的下降。
③一個節點能存放的數據要比指針少一,數據保存率太低
三.使用B+樹作為索引
B+ 樹是一種樹數據結構,通常用于數據庫和操作系統的文件系統中。B+ 樹的特點是能夠保持數據穩定有序,其插入與修改擁有較穩定的對數時間復雜度。B+ 樹元素自底向上插入,這與二叉樹恰好相反。
B+ 樹在節點訪問時間遠遠超過節點內部訪問時間的時候,比可作為替代的實現有著實在的優勢。這通常在多數節點在次級存儲比如硬盤中的時候出現。通過最大化在每個內部節點內的子節點的數目減少樹的高度,平衡操作不經常發生,而且效率增加了。這種價值得以確立通常需要每個節點在次級存儲中占據完整的磁盤塊或近似的大小。
如圖是一個B+樹,這時候如果我們要查詢46這個節點,那么只需要在第一節點中進行查找,發現46在45和67之間,然后如何就走第二個節點,走到了(45,48,63)這個節點,然后發現46在45-48之間,走第一個節點,如何遍歷,就找到了46。
①使用B+樹,一個節點可以存儲的值和它指向一下個節點的指針一樣多,可以多存儲數據。
②所有父節點的數據同時也保存在子節點之中,這樣所有數據都在一起,比較連續。
③葉子節點的所有值由一個鏈表進行連接,這樣如果進行范圍查詢就非常簡單,只需順著鏈表往下走就行。
四.Hash和BitMap索引
1.Hash索引
Hash索引是指,將要創建索引的字段值進行hash,按照hash值為key,數據地址為value,保存成為一個HashMap結構,如果一個hash值對應多個行數據,就進行鏈表存儲。
①使用hash只需要進行一次hash就可以查找到值,不需要進行像二叉樹一樣的逐層查找,效率高。
②由于數據是進行hash之后進行保存的,所以原數據在里面是沒有順序的,所以僅僅能滿足 “=” 、“IN”這樣的操作,不能使用范圍查詢 。
③無法被用來避免數據的排序操作,因為不像B+樹,它的存儲是有順序的,是按照索引值進行排序的,然后hash索引是hash后的數據,所以無法避免排序。
④不能利用部分索引鍵查詢,在B+樹中,多個字段的索引是通過按照字段一次排序進行存儲的,所以如果創建了(A,B,C)三個字段的聯合索引,那么可以使用部分索引例如A,(A,B),但是hash的聯合索引是所有字段一起進行hash的結果去映射的,所以部分字段無法使用。
⑤不能避免全表掃描。因為查詢出來的數據還是存在buckets中的,還是要進行表的遍歷。
⑥遇到大量hash值相等的情況后,也就是出現大量的hash碰撞后,鏈表就會非常長,變成線性結構,查詢效率并不一定會比B+-Tree高。
2.BitMap索引
BitMap索引使用不同的值進行分組,然后每組數據中將相同的值的位置標位1,不同的值的位置標為0,這樣就會形成很多個不同的二進制串。
①BitMap是對數據進行位圖存儲,所以查詢會很快。
②由于每個值都需要一個位圖去保存,所以只適用于值的種類比較少的情況,比如性別這樣的字段。
③進行數據的統計速度快。
④鎖的力度大,因為進行增刪改的時候,位圖的值的位置會發生變化,所以會鎖住整個位圖的對象。
五.密集索引和稀疏索引和覆蓋索引
1.密集索引
①在InnoDB引擎中,數據本身就是索引文件,其表數據文件本身就是B+樹組織的一個索引結構,樹的葉節點data域保存了完整的數據記錄。這個索引的key是數據表的主鍵,因此InnoDB表數據文件本身就是主索引。這種索引被稱為"聚集索引(或者聚族索引)"。
②MyISAM引擎的索引不是聚集索引。
③InnoDB有且只有一個聚集索引,選取規則如下:
A:如果定義了主鍵,那么該主鍵就作為密集索引。
B: 如果沒有定義主鍵,則選擇該表的第一個唯一非空索引作為密集索引。
C:如果上面都不滿足,InnoDB會在內部生成一個六字節的自增值作為隱藏主鍵,來作為密集索引。
④密集索引文件中的每個搜索碼值都對應一個索引值。
⑤密集索引決定了數據的物理存放順序,一個文件只能有一個物理存放順序,所以只能有一個密集索引。
⑥密集索引的葉子節點之間存放的該行數據。
2.稀疏索引
①MyISAM引擎的所有索引都是稀疏索引。
②稀疏索引文件只為搜索碼的某些值建立索引。
③葉子節點只保留了數據的地址或者主鍵,獲取到了之后還要跟地址或者主鍵去再次進行查找。
3.覆蓋索引
如果一個索引包含(或者說覆蓋)所有需要查詢的字段的值,那么這個索引就是"覆蓋索引"。
①覆蓋索引會把要查詢出來的列和索引進行對應,查詢時不會進行回表操作(不會進行二次查詢)。
六.InnoDB和MyISAM的區別
①數據的物理存放不同。
InnoDB在文件存放中會存在兩個文件,一個frm結尾的文件保存了這個表的定義文件,數據和索引都存放在一個以ibd結尾的文件中。
MyISAM在文件存放中存在三個文件,一個frm結尾的文件保存了這個表的定義文件,索引存放在一個以MYI結尾的文件中,數據存放在一個以MYD結尾的文件中。
②索引不同
InnoDB存在一個密集索引,葉子節點保存的該列的所有值;MyISAM所有的索引都是稀疏索引,葉子節點保存的是數據的地址;InnoDB稀疏索引的葉子節點存放的是主鍵。
③MyISAM緩存有表meta-data(行數等信息),所以在進行count(*)的時候會很快,InnoDB沒有。
④MyISAM強調的是性能,每次查詢具有原子性,其執行速度比InnoDB類型更快。
⑤MyISAM不支持事務操作。InnoDB支持事務。
⑥MyISAM不支持外鍵,二InnoDB支持。
七.索引的創建條件
1.需要創建索引的情況
①主鍵自動建立唯一索引。
②頻繁作為查詢條件的字段應該創建索引。
③查詢中與其他表關聯的字段,外鍵等應該建立索引。
④頻繁更新的字段不適合創建索引。
⑤where條件里用不到的字段不創建索引。
⑥在單值索引和復合索引之間,優先選擇復合索引。
⑦查詢中排序的字段,應該建立索引。
⑧查詢中進行統計或者分組的字段應該建立索引。
2.不需創建索引的情況
①表記錄太少的情況。
②經常進行增刪改的表。
③數據重復且分布均勻的表字段。
八.explain指令
1.能干什么
使用explain關鍵字可以模擬優化器執行SQL查詢語句,從而知道MySQL是如何處理你的SQL語句的,分析你的查詢語句或是表結構的性能瓶頸。
通過explain關鍵字可以查看如下的一些信息。
①表的讀取順序。
②數據讀取操作的操作類型。
③那些索引可以使用。
④那些索引實際被使用。
⑤表之間的引用關系。
⑥每張表有多少行被執行器查詢。
2.怎么用
在SQL語句前面加入explain 關鍵字,執行該語句就可以獲取信息。
①id
select查詢的序號列,包含一組數字,表示查詢中執行select子句或操作表的順序。
A : id相同,表示順序由上而下執行。
B : id不同,如果是子查詢,id的序號會遞增,id值越大優先級越高,越先被執行。
C : id相同不同,同時存在。
②select_type
查詢的類型,主要用于區別 普通查詢、聯合查詢、子查詢等的復雜查詢。有下面幾種值:
A :SIMPLE 簡單的select語句,查詢中不包含子查詢或者union。
B : PRIMARY 查詢中若包含任何復雜的子查詢,最外層查詢則被標記為PRIMARY.
C : SUBQUERY 在select或者where列表中包含子查詢,會被標記為SUBQUERY。
D : DERIVED 在from列表中包含的子查詢被標記為DERIVED(衍生),mysql會遞歸執行這些子查詢,把結果放在臨時表里。
E : UNION 若第二個select出現在union之后,則被標記為union,若union包含在from子句的子查詢中,外層select被標記為DERIVED。
F : UNION RESULT 從UNION表獲取結果的select。
③table
顯示這一行的數據是關于那張表的。
④type
顯示查詢使用了何種類型,從走好到最差依次是下面順序。
A : system :表示只有一行記錄,這是const類型的特例,平時不會出現,這個可以忽略不計。
B : const : 表示通過索引一次就找到了,const用于表示primary key或者unique索引。因為只匹配一行數據,所以很快。
C : eq_ref :唯一性索引掃描,對應每個索引鍵,表中只有一條記錄與之匹配。常見于主鍵或唯一索引掃描。
D : ref :非唯一索引掃描,返回匹配某個單獨值的所有行;本質上也是一種索引訪問,它返回所有匹配某個單獨值的行,然而,它可能會找到很多個符合條件的行,所以他應該屬于查找和掃描的混合體。
E : range :只檢索給定范圍的值,使用一個索引選擇行。key列顯示使用了那個索引,一般就是在where語句中出現了between、<、>、in等的查詢。這種范圍掃描索引比全表掃描要好,因為它只需要開始于索引的某一點,而結束于另一點,不用掃描全部索引。
F : index :Full Index Scan,index與All的區別為index類型只遍歷索引樹。這通常比All快,因為索引文件通常比數據文件少。也就是說雖然All和Index都是讀全表,但index是從索引里面讀,而all是從硬盤里面讀。
G : all :Full Table Scan,將遍歷全表找到匹配的行。
一般來說,得保證查詢至少達到range級別,最好達到ref級別。
⑤possible_keys
顯示可能在這張表表中的索引,一個或多個。查詢涉及到的字段上若存在索引,則該索引被列出,但不一定被查詢實際使用.
⑥key
實際使用到的索引,如果為null,則表示沒有使用索引。查詢中若使用覆蓋索引,則索引和查詢的select字段重疊。
⑦key_len
表示索引中使用的字節數,可以通過該列計算查詢中使用的索引的長度,在不損失精確性的情況下,長度越短越好。 key_len顯示的值為索引字段的最大可能長度,并非實際使用長度,即key_len使根據表定義計算而得的,不是通過表內檢索出來的。
⑧ref
顯示索引的哪一列被使用了,如果可能的話,是一個常數,那些列或常量被用于查詢索引上面的值。
⑨rows
根據表統計信息及索引選用情況,大致估算出找到所需記錄需要讀取的行數。
⑩Extra
包含不合適在其他列中顯示但十分重要的額外信息。有下面的一些取值:
①Using filesort : 說明mysql 會對數據使用一個外部排序,而不是按照表內的索引順序就行排序。MYSQL中無法利用索引完成的排序操作成為"文件排序"。
②Using temporary :使用了臨時表保存中間結果,mysql在對查詢結果排序時使用臨時表。常見于排序order by和分組查詢group by.。
③Using index : 表示相應的select操作中使用了覆蓋索引,避免訪問了表的數據行,效率不錯;如果同時出現using where ,表明索引被用來執行索引鍵值的查找;如果沒有同時出現using where,表明索引用來讀取數據而非執行查找動作。
④Using where : 表明使用了where過濾。
⑤Using join buffer :表明使用了連接緩存。
⑥impossible where :where子句的值總是false,不能用來獲取任何元組。
⑦select tables optimized away :在沒有group by子句的情況下,基于索引優化MIN/MAX操作或者對于MyISAM存儲引擎優化count(*)操作,不必等到執行階段再進行計算,查詢執行計劃生成的階段即完成優化。
⑧distinct :優化distinct操作,在找到第一匹配的元組后即停止找同樣值的動作。
九.索引失效
①復合索引全值匹配最好。
②最左前綴匹配原則。
③不在索引列上做任何操作(計算、函數、類型轉換),會導致索引失效而轉向全表掃描。
④存儲引擎不能使用索引范圍中范圍條件右邊的列(比如索引(A,B,C),如果查詢條件為A=xxx and B > xx and C = xxx,則只會使用索引A,B使用了范圍查詢,不會使用C)。
⑤盡量使用覆蓋索引,減少select *。
⑥mysql 在使用不等于(!= 或者 <>)的時候無法使用索引會導致全表掃描。
⑦is null、is not null也無法使用索引。
⑧like以通配符開頭('%abc')mysql會使用全表掃描。
⑨字符串不加單引號索引失效。
⑩少用or,用它來連接會導致索引失效。
建議
①對于單值索引,盡量選擇針對當前query過濾性更好的索引。
②在選擇組合索引的時候,當前query中過濾性最好的字段在索引字段順序中,位置越靠前越好。
③在選擇組合索引的時候,盡量選擇可以能夠包含當前query中的where子句中更多字段的索引。
④盡可能通過分析統計信息和調整query的寫法來達到選擇合適索引的目的。
十.最左前綴匹配原則
①mysql會一直向右匹配直到遇到范圍查詢(>、<、between、like)就停止匹配,比如 a=3 and b=4 and c>5 and d=6,如果建立(a,b,c,d)順序的索引,d就用不到索引;如果建立(a,b,d,c)的索引則都可以用到,a,b,d的順序是可以隨意調整的。這就是最左匹配原則。
②= 和 in 是可以亂序的,比如 a=1 and b=2 and c=3,建立(a,b,c)索引可以任意順序,mysql查詢優化器水幫助我們調整順序。
③為什么?
因為mysql對于聯合索引的處理是,首先根據第一個字段進行排序,然后在排序的基礎上再根據第二個字段進行排序,類似于group by 對多個字段進行排序,所以對于第一個字段(最左)來說是絕對有序的,但是對于后面的字段來說就是無序的了。這就是最左匹配原則的成因。
十一.查詢優化
1.用于小表驅動大表。
①驅動表的定義
當進行多表連接查詢時, [驅動表] 的定義為:
1)指定了聯接條件時,滿足查詢條件的記錄行數少的表為[驅動表]。
2)未指定聯接條件時,行數少的表為[驅動表](Important!)。
②為什么?
可以使用嵌套循環來進行類比
for(int i=5;.......)
{
for(int j=1000;......)
{}
}
如果小的循環在外層,對于數據庫連接來說就只連接5次,進行5000次操作,如果1000在外,則需要進行1000次數據庫連接,從而浪費資源,增加消耗。這就是為什么要小表驅動大表。
③怎么做
下面結論都是針對in或exists的。
in后面跟的是小表,exists后面跟的是大表。
對于exists
select .....from table where exists(subquery);
可以理解為:將主查詢的數據放入子查詢中做條件驗證,根據驗證結果(true或false)來決定主查詢的數據是否得以保留。
2.order by關鍵字優化
①order by子句,盡量使用index方式排序,避免使用filesort排序。
②盡可能在索引列上完成排序操作,遵照索引建的最佳左前綴原則。
③如果不在索引列上,filesort有兩種算法:mysql會啟用雙路排序和單路排序。
A : 雙路排序:mysql4.1之前使用雙路排序,字面意思就是兩次掃描磁盤,最終得到數據,讀取
行指針和order by列,對他們進行排序,然后掃描已經排序好的列表,按照列表中的值重新從列表中讀取對應的數據輸出。簡單來說就是:從磁盤取排序字段,然后在buffer中進行排序,再從磁盤取其它字段。
B : 從磁盤中讀取查詢需要的所有列,按照order by列在buffer對它們進行排序,然后掃描排序后的列表進行輸出,它的效率要快一些,避免了第二次讀取數據,并且把隨機IO變成了順序IO,但它會使用更多的空間。
④優化策略
- 增大sort_offset_size 參數的設置。
- 增大max_length_for_sort_data 參數的設置。
3.group by優化
①group by實質是先排序再進行分組,遵照索引建的最佳左前綴原則。
②當無法使用索引列,增大max_length_for_sort_data參數的設置+增大sort_buffer_size參數的設置。
③where高于having,能寫在where限定的條件就不要去having限定了。