Mysql優化---基於索引優化(B-Tree與B+Tree)

一、索引是什麼?

1.MySQL官方對索引的定義爲:索引(Index)是幫助MySQL高效獲取數據的數據結構,即索引的本質是一種數據結構

可以簡單理解爲:排好序快速查找數據結構

詳解:在數據之外,數據庫系統還維護着滿足特定查找算法的數據結構---索引,這些數據結構是以某種方式引用(指向)數據,這樣就可以在這些數據結構上實現高級的查找算法。

左邊是數據表,一共有兩列七行數據,最左邊存儲的是內存地址。

 爲了加快Col2的查找,可以維護一個右邊所示的二叉查找樹,每個節點分別包含索引鍵值和一個指向對應數據記錄物理地址的指針,這樣就可以運用二叉查找在一定的複雜度內獲取到相應數據,從而快速的檢索出符合條件的記錄。

二叉樹弊端之一:二叉樹很可能會發生兩邊不平衡的情況。

2.簡單提一下MySQL索引的類型

B-TREE: (B:balance)  會自動根據兩邊的情況自動調節,使兩端無限趨近於平衡狀態。可以使性能最穩定。
    B-TREE弊端:(插入/修改操作多時,B-TREE會不斷調整平衡,消耗性能)從側面說明了索引不是越多越好。
B+TREE:MySQL應用的索引結構

(下面會繼續提到)

3.一般來說索引本身也很大,不可能全部存儲在內存中,因此索引往往以索引文件的形式存儲的磁盤上(myi文件)

我們平常所說的索引,如果沒有特別指明,都是指B樹(多路搜索樹,並不一定是二叉的)結構組織的索引。

其中聚集索引,次要索引,覆蓋索引,複合索引,前綴索引,唯一索引默認都是使用B+樹索引,統稱索引。當然,除了B+樹這種類型的索引之外,還有哈稀索引(hash index)等。

4.索引的優勢與劣勢

優勢:1)類似大學圖書館建書目索引,提高數據檢索的效率,降低數據庫的IO成本

2)通過索引列對數據進行排序,降低數據排序的成本,降低了CPU的消耗

劣勢:1)實際上索引也是一張表,該表保存了主鍵與索引字段,並指向實體表的記錄,所以索引列也是要佔用空間的

2)雖然索引大大提高了查詢速度,同時卻會降低更新表的速度,如對錶進行INSERT、UPDATE和DELETE。因爲更新表時,MySQL不僅要保存數據,還要保存一下索引文件每次更新添加了索引列的字段,都會調整因爲更新所帶來的鍵值變化後的索引信息

3)索引只是提高效率的一個因素,如果你的MySQL有大數據量的表,就需要花時間研究建立最優秀的索引,或優化查詢語句

二、索引類型

1.主鍵索引

設定爲主鍵後,數據庫會自動建立索引,Innodb存儲引擎會默認將在主鍵上建立一個聚簇索引(下面會解釋聚簇索引)。

2.單值索引

一個索引只包含單個列,一個表可以有多個單行索引

3.唯一索引

即建立unique約束時,會自動創建一個唯一索引

4.複合索引,即一個索引包含多個列。在數據庫操作期間,複合索引比單值索引所需要的開銷更小(對於相同的多個列建索引)
當表的行數遠大於索引列的數目時可以使用複合索引。

三、索引結構

MySQL使用的索引結構爲B+樹索引,對我們Java開發工程師來說,我們無需關注太多類似於full-text全文索引、Hash索引、R-tree索引等其他索引,我們只需要關注最重要的兩個:B-Tree索引和B+Tree索引。

1.首先我們先來討論一個問題,文章剛開始時提到的圖裏索引結構爲一個二叉樹結構,那麼爲什麼MySQL不使用那種二叉樹作爲索引結構呢?

如果用二叉查找樹作爲MySQL的索引結構,那假如我們有一百萬條數據,這個樹的深度可想而知,運氣好第一層就找到了,運氣不好,得進行多少次IO讀寫才能找到我們希望找到的數據?所以不能採用二叉樹,因爲它長得太高(深度太深),導致I/O讀寫過於頻繁。

如何解決?

二叉樹又高又瘦,因爲它的高是導致影響讀寫效率最主要的因素,那麼非得用二叉樹嗎?我們可以採用多叉樹啊,讓它胖一點沒關係,主要是矮啊!只要這個樹深度夠短,我們就可以通過極少的I/O讀寫來訪問到我們要查找的數據,所以:B-樹與B+樹由此而生。

2.B-樹

B-樹也叫B樹,‘-’只是一個符號,大家不要誤會,所以提到BTree一般就是指的B-Tree。

每一個藍色的關鍵字節點都又會去指向一個數據庫的實際數據,只不過在這裏沒有體現,可以把這些藍色的關鍵字節點理解爲一個表中某字段的數據。

B-樹的搜索,從根結點開始,對結點內的關鍵字(有序)序列進行二分查找,如果命中則結束,否則進入查詢關鍵字所屬範圍的兒子結點;重複,直到所對應的兒子指針爲空,或已經是葉子結點;

B-樹的特性:

1).關鍵字集合分佈在整顆樹中;

2).任何一個關鍵字出現且只出現在一個結點中;

3).搜索有可能在非葉子結點結束

4).其搜索性能等價於在關鍵字全集內做一次二分查找;

3.B+樹(MySQL的索引結構)

B+樹是應文件系統所需而產生的一種B樹的變形樹(文件的目錄一級一級索引,只有最底層的葉子節點(文件)保存數據非葉子節點只保存索引,不保存實際的數據,數據都保存在葉子節點中,這不就是文件系統文件的查找嗎?

我們就舉個文件查找的例子:有3個文件夾a、b、c, a包含b,b包含c,一個文件yang.c,a、b、c就是索引(存儲在非葉子節點), a、b、c只是要找到的yang.c的key,而實際的數據yang.c存儲在葉子節點上。

所有的非葉子節點都可以看成索引部分!

非葉子節點(比如5,28,65)只是一個key(索引),實際的數據存在葉子節點上(5,8,9)纔是真正的數據(指向數據庫真實數據的指針)。

MySQL索引實現

在MySQL中,索引屬於存儲引擎級別的概念,不同存儲引擎對索引的實現方式是不同的,本文主要討論MyISAM和InnoDB兩個存儲引擎的索引實現方式。

MyISAM索引實現

MyISAM引擎使用B+Tree作爲索引結構,葉節點的data域存放的是數據記錄的地址。下圖是MyISAM索引的原理圖:

                                                                                        圖8

這裏設表一共有三列,假設我們以Col1爲主鍵,則圖8是一個MyISAM表的主索引(Primary key)示意。可以看出MyISAM的索引文件僅僅保存數據記錄的地址。在MyISAM中,主索引和輔助索引(Secondary key)在結構上沒有任何區別,只是主索引要求key是唯一的,而輔助索引的key可以重複。如果我們在Col2上建立一個輔助索引,則此索引的結構如下圖所示:

                                                                                      圖9

同樣也是一顆B+Tree,data域保存數據記錄的地址。因此,MyISAM中索引檢索的算法爲首先按照B+Tree搜索算法搜索索引,如果指定的Key存在,則取出其data域的值,然後以data域的值爲地址,讀取相應數據記錄。

MyISAM的索引方式也叫做“非聚集”的,之所以這麼稱呼是爲了與InnoDB的聚集索引區分。

InnoDB索引實現

雖然InnoDB也使用B+Tree作爲索引結構,但具體實現方式卻與MyISAM截然不同。

第一個重大區別是InnoDB的數據文件本身就是索引文件。從上文知道,MyISAM索引文件和數據文件是分離的,索引文件僅保存數據記錄的地址。而在InnoDB中,表數據文件本身就是按B+Tree組織的一個索引結構,這棵樹的葉節點data域保存了完整的數據記錄。這個索引的key是數據表的主鍵,因此InnoDB表數據文件本身就是主索引。

                                                                         圖10

圖10是InnoDB主索引(同時也是數據文件)的示意圖,可以看到葉節點包含了完整的數據記錄。這種索引叫做聚集索引。因爲InnoDB的數據文件本身要按主鍵聚集,所以InnoDB要求表必須有主鍵(MyISAM可以沒有),如果沒有顯式指定,則MySQL系統會自動選擇一個可以唯一標識數據記錄的列作爲主鍵,如果不存在這種列,則MySQL自動爲InnoDB表生成一個隱含字段作爲主鍵,這個字段長度爲6個字節,類型爲長整形。

第二個與MyISAM索引的不同是InnoDB的輔助索引data域存儲相應記錄主鍵的值而不是地址。換句話說,InnoDB的所有輔助索引都引用主鍵作爲data域。例如,圖11爲定義在Col3上的一個輔助索引:

圖11

這裏以英文字符的ASCII碼作爲比較準則。聚集索引這種實現方式使得按主鍵的搜索十分高效,但是輔助索引搜索需要檢索兩遍索引:首先檢索輔助索引獲得主鍵,然後用主鍵到主索引中檢索獲得記錄。

瞭解不同存儲引擎的索引實現方式對於正確使用和優化索引都非常有幫助,例如知道了InnoDB的索引實現後,就很容易明白爲什麼不建議使用過長的字段作爲主鍵,因爲所有輔助索引都引用主索引,過長的主索引會令輔助索引變得過大。再例如,用非單調的字段作爲主鍵在InnoDB中不是個好主意,因爲InnoDB數據文件本身是一顆B+Tree,非單調的主鍵會造成在插入新記錄時數據文件爲了維持B+Tree的特性而頻繁的分裂調整,十分低效,而使用自增字段作爲主鍵則是一個很好的選擇。

4.爲什麼說B+樹比B-樹更適合做數據庫索引?

1)B+樹的磁盤讀寫代價更低:B+樹的內部節點並沒有指向關鍵字具體信息的指針,因此其內部節點相對B樹更小,如果把所有同一內部節點的關鍵字存放在同一盤塊中,那麼盤塊所能容納的關鍵字數量也越多,一次性讀入內存的需要查找的關鍵字也就越多,相對IO讀寫次數就降低了。(如果你看不懂這句話的意思,我負責給你解釋之前B-Tree是將真實數據存儲到了非葉子節點中,現在B+Tree把存儲真實數據的空間拿來去存儲了引用,當利用索引查詢時,需要從磁盤中加載這個節點到內存中去比較的時候,加載的需要查找的關鍵字也就比B-Tree要多,所以I/O讀寫次數自然就相對較低

2)B+樹的查詢效率更加穩定:由於非終結點並不是最終指向文件內容的結點,而只是葉子結點中關鍵字的索引。所以任何關鍵字的查找必須走一條從根結點到葉子結點的路。所有關鍵字查詢的路徑長度相同,導致每一個數據的查詢效率相當。

3)由於B+樹的數據都存儲在葉子結點中,分支結點均爲索引,方便掃庫,只需要掃一遍葉子結點即可,但是B樹因爲其分支結點同樣存儲着數據,我們要找到具體的數據,需要進行一次中序遍歷按序來掃,所以B+樹更加適合在區間查詢的情況,所以通常B+樹用於數據庫索引。

 

PS:我在知乎上看到有人是這樣說的,我感覺說的也挺有道理的:

他們認爲數據庫索引採用B+樹的主要原因是:B樹在提高了IO性能的同時並沒有解決元素遍歷的我效率低下的問題,正是爲了解決這個問題,B+樹應用而生。B+樹只需要去遍歷葉子節點就可以實現整棵樹的遍歷。而且在數據庫中基於範圍的查詢是非常頻繁的,而B樹不支持這樣的操作或者說效率太低。

 

總結:所以要對查找的方式進行優化,熟悉的二分查找,二叉樹可以把速度提升到O(log(n,2)),查詢的瓶頸在於樹的深度,最壞的情況要查找到二叉樹的最深層,由於,每查找深一層,就要訪問更深一層的索引文件。在多達數G的索引文件中,這將是很大的開銷。所以,儘量把數據結構設計的更爲‘矮胖’一點就可以減少訪問的層數。在衆多的解決方案中,B-/B+樹很好的適合。B-樹定義具體可以查閱,簡而言之就是中間節點可以多餘兩個子節點,而且中間的元素可以是一個域。相比B-樹,B+樹的父節點也必須存在於子節點中,是其中最大或者最小元素,B+樹的節點只存儲索引key值,具體信息的地址存在於葉子節點的地址。這就使以頁爲單位的索引中可以存放更多的節點。減少更多的I/O支出。因此,B+樹成爲了數據庫比較優秀的數據結構,MySQL中MyIsAM和InnoDB都是採用的B+樹結構。不同的是前者是非聚簇索引,後者主鍵是聚簇索引,所謂聚簇索引是物理地址連續存放的索引,在取區間的時候,查找速度非常快,但同樣的,插入的速度也會受到影響而降低。非聚集索引的物理位置使用鏈表來進行存儲。

四、在平時開發中,哪些情況需要建立索引,哪些情況不需要建立索引呢?

需要建立索引的情況

1.主鍵會自動建立唯一索引,且在Innodb存儲引擎下創建聚簇索引。

2.頻繁作爲查詢條件的字段應建立索引(where後面的子句)

3.查詢中與其他表關聯的字段,外鍵關係應建立索引。

如A 表關聯 B 表:A join B  。  on 後面的連接條件 既 A 表查詢 B 表的條件。所以 B 表被關聯的字段建立索引能大大提高查詢效率
因爲在 join 中,join 左邊的表會用每一個字段去遍歷 B 表的所有的關聯數據,相當於一個查詢操作

4.單鍵/組合索引的選擇問題,who?(在高併發下傾向創建組合索引)

5.查詢中排序和分組的字段,若通過索引去訪問將大大提高排序速度。即group by 和 order by 後面的字段有索引大大提高效率。

不需要建立索引的情況

1.表中記錄很少

2.經常進行增刪改的表。

Why?建立索引會提高查詢速度,同時卻會降低更新表的速度,如對錶進行INSERT、UPDATE和DELETE。因爲更新表時,MySQL不僅要保存數據,還要保存一下索引文件。

3.Where條件裏用不到的字段不創建索引

4.數據重複且分佈平均的表字段,因此應該只爲最經常查詢和最經常排序的數據列建立索引。注意,如果某個數據列包含許多重複的內容,爲它建立索引就沒有太大的實際效果。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章