【mysql】Mysql性能優化基礎之索引數據結構類型Btree和Hash的深入探討

我們大家都是知道,如果數據庫遇到查詢性能問題,就會去檢查是否命中了索引,但大家是否真正瞭解過mysql底層數據及索引結構是怎麼存儲的嗎?下面就來逐步談到一下;首先我們來看看一些數據結構,分別來比較一下爲什麼mysql開發人員選擇B+tree,而不選擇其他數據結構存儲;

各個數據結構對比

二叉查找樹(Binary Search Tree)

二叉查找樹(Binary Search Tree),(又:二叉搜索樹,二叉排序樹)它或者是一棵空樹,或者是具有下列性質的二叉樹,如果我們數據庫索引結構採用二叉樹結構存儲,如圖所示,會導致我們的樹層級會越來越高,當然也起不到索引的效果,數據量越多樹就越高;
特點:
a. 若它的左子樹不空,則左子樹上所有結點的值均小於它的根結點的值;
b. 若它的右子樹不空,則右子樹上所有結點的值均大於它的根結點的值;
c. 它的左、右子樹也分別爲二叉排序樹。
二叉查找樹數據結構
假如我們這裏有1000W的排序數據存入,如果纔有這種查找方式,則最大有1000W次查詢,顯然是不可取的,相當於全表掃描了!那麼我們如何減少樹高呢?再看看下一個數據結構;

紅黑樹(Red/Black Tree)

平衡二叉樹,通過對任何一條從根到葉子的簡單路徑上各個節點的顏色進行約束,確保沒有一條路徑會比其他路徑長2倍,因而是近似平衡的。所以相對於嚴格要求平衡的AVL樹來說,它的旋轉保持平衡次數較少。用於搜索時,插入刪除次數多的情況下我們就用紅黑樹來取代AVL。雖然紅黑樹相比傳統的二叉樹減少了層級;但在1000W樹量基礎上 2^n =1000W,
n也約等於31,及如果mysql索引採用該數據結構,最多需要31次IO開銷才能找到需要的數據,也非常耗性能;那麼我們思考一下如何進一步減少樹高呢?我想大家已經想到了,就是在單個樹節點上多存儲一些數據,做到水平擴展;樹高不就減少了嗎!接下來就要介紹另外一種樹形結構BTree;
紅黑樹數據結構

B-Tree

B-tree(多路搜索樹,並不是二叉的)是一種常見的數據結構。使用B-tree結構可以顯著減少定位記錄時所經歷的中間過程,從而加快存取速度。按照翻譯,B 通常認爲是Balance的簡稱。這個數據結構一般用於數據庫的索引,綜合效率較高。
B-tree中,每個結點包含:
1、本結點所含關鍵字的個數;
2、指向父結點的指針;
3、關鍵字;
4、指向子結點的指針;
對於一棵m階B-tree,每個結點至多可以擁有m個子結點。各結點的關鍵字和可以擁有的子結點數都有限制,規定m階B-tree中,根結點至少有2個子結點,除非根結點爲葉子節點,相應的,根結點中關鍵字的個數爲1m-1;非根結點至少有[m/2]([],向上取整)個子結點,相應的,關鍵字個數爲[m/2]-1m-1。

B-tree有以下特性:
1、關鍵字集合分佈在整棵樹中;
2、任何一個關鍵字出現且只出現在一個結點中;
3、搜索有可能在非葉子結點結束;
4、其搜索性能等價於在關鍵字全集內做一次二分查找;
5、自動層次控制;
由於限制了除根結點以外的非葉子結點,至少含有M/2個兒子,確保了結點的至少利用率,其最低搜索性能爲:
其中,M爲設定的非葉子結點最多子樹個數,N爲關鍵字總數;
所以B-樹的性能總是等價於二分查找(與M值無關),也就沒有B樹平衡的問題;
由於M/2的限制,在插入結點時,如果結點已滿,需要將結點分裂爲兩個各佔M/2的結點;刪除結點時,需將兩個不足M/2的兄弟結點合併。B-tree數據結構圖
這樣得數據結構又比紅黑樹更好,因爲它們分支多層數少,都知道磁盤IO是非常耗時的,而像大量數據存儲在磁盤中所以我們要有效的減少磁盤IO次數避免磁盤頻繁的查找。這樣其實還是有問題,每個結點都存儲有數據,存儲空間也是很大不說,如果存在頻繁插入數據,會導致數據結構變化導致都排序開銷,當然我們mysql存儲引擎更加優化了一下,用的是B+tree,什麼又叫B+tree呢?接下來繼續介紹;

B+Tree

B+樹(B+Tree)是B樹(B-tree)的變種樹,有n棵子樹的節點中含有n個關鍵字,每個關鍵字不保存數據,只用來索引,數據都保存在葉子節點。是爲文件系統而生的。這樣就會大大減少磁盤IO的讀寫操作;結構圖如下,當然mysql開發者們在B+Tree存儲索引及數據方面做了些優化,不同的數據存儲引擎存儲方式不一樣;
B+Tree數據結構圖我們都知道mysql有兩個常用都存儲引擎(MyISAM和Innodb),細心的朋友可能發現,mysql庫中的表,他們的物理存儲文件會不一樣;如下圖所示:
查看mysql庫表結構
下面來解釋一下兩張表不同文件代表的意思,首先是user表,因爲user表使用的存儲引擎爲MyISAM

  1. user.frm-----------這個是存儲表結構的;
  2. user.MYD------------是存儲錶行列數據的;
  3. user.MYI-------------是存儲索引的,當我們查找到數據索引後會先加載這個,然後在索引文件中找到對應的數據存儲文件指針,這裏即指向user.MYD數據文件;

而time_zone_name表,使用的存儲引擎是Innodb

  1. time_zone_name.frm ------------這個是存儲表結構的,通user.frm 相同;
  2. time_zone_name.idb------------這個是存儲索引和數據的集合體,數據和索引在一起存儲,這樣的索引常稱爲聚集索引,當然也有非聚集索引。mysql有且僅一個聚集索引(即一個主鍵,如果你沒有創建組建,系統會自動創建一個主鍵),其作用是一個排好序的數據,加快檢索速度;爲什麼只能是一個聚集索引呢?原因也不難理解,排序只能按照一種規則進去排序下去;

存儲引擎MyISAM與Innodb區別對比

MyISAM存儲引擎

  1. 不支持事務,但是整個操作是原子性的(事務具備四種特性:原子性、一致性、隔離性、持久性)

  2. 不支持外鍵,支持表鎖,每次所住的是整張表
    MyISAM的表鎖有讀鎖和寫鎖(兩個鎖都是表級別):
    表共享讀鎖和表獨佔寫鎖。在對MyISAM表進行讀操作時,不會阻塞其他用戶對同一張表的讀請求,但是會阻塞其他用戶對錶的寫請求;對其進行寫操作時會阻塞對同一表讀操作和寫操作;
    MyISAM存儲引擎的讀鎖和寫鎖是互斥的,讀寫操作是串行的。那麼,一個進程請求某個MyISAM表的讀鎖,同時另一個進程也請求同一表的寫鎖,MySQL如何處理呢?答案是寫進程先獲得鎖。不僅如此,即使讀請求先到鎖等待隊列,寫請求後到,寫鎖也會插到讀鎖請求之前!這是因爲MySQL認爲寫請求一般比讀請求要重要。這也正是MyISAM表不太適合於有大量更新操作和查詢操作應用的原因,因爲,大量的更新操作會造成查詢操作很難獲得讀鎖,從而可能永遠阻塞。這種情況有時可能會變得非常糟糕!

  3. 一個MyISAM表有三個文件:索引文件,表結構文件,數據文件

  4. 存儲表的總行數,執行select count() from table時只要簡單的讀出保存好的行數即可,(myisam存儲引擎的表,count()速度快的也僅僅是不帶where條件的count。這個想想容易理解的,因爲你帶了where限制條件,原來所以中緩存的表總數能夠直接返回用嗎?不能用。這個查詢引擎也是需要根據where條件去表中掃描數據,進行統計返回的。)

  5. 採用非聚集索引,索引文件的數據域存儲指向數據文件的指針。輔索引與主索引基本一致,但是輔索引不用保證唯一性。

  6. 支持全文索引和空間索引

  7. 對於AUTO_INCREMENT類型的字段,在MyISAM表中,可以和其他字段一起建立聯合索引。
    MyISAM的主索引圖:索引文件的每個數據域存儲指向數據文件的指針(每個索引指向了數據地址)
    MyISAM的主索引圖
    MyISAM的輔索引:索引文件的每個數據域存儲指向數據文件的指針(每個索引指向了數據地址),輻索引不用保證唯一性
    在這裏插入圖片描述

Innodb存儲引擎:

  1. 支持事務,支持事務的四種隔離級別;是一種具有事務(commit)、回滾(rollback)和崩潰修復能力(crash recovery capabilities)的事務安全(transaction-safe (ACID compliant))型表。

  2. 支持行鎖和外鍵約束,因此可以支持寫併發

  3. 不存儲總行數;也就是說,執行select count() from table時,InnoDB要掃描一遍整個表來計算有多少行。注意的是,當count()語句包含 where條件時,兩種表的操作是一樣的。

  4. 對於AUTO_INCREMENT類型的字段,InnoDB中必須包含只有該字段的索引

  5. DELETE FROM table時,InnoDB不會重新建立表,而是一行一行的刪除

  6. 一個Innodb表存儲在一個文件內(共享表空間,表大小不受操作系統的限制),也可能爲多個(設置爲獨立表空間,表大小受操作系統限制,大小爲2G),受操作系統文件大小的限制

  7. 主鍵索引採用聚集索引(索引的數據與存儲數據文件是本身),輔索引的數據域存儲主鍵的值;因此從輔索引查找數據,需要先通過輔索引找到主鍵值,再訪問主鍵索引;最好使用自增主鍵,防止插入數據時,爲維持B+樹結構,文件的大調整。
    Innodb的主索引圖:(索引位置上存儲的直接是數據本身)
    在這裏插入圖片描述
    Innodb的輔索引圖:在這裏插入圖片描述

總結大圖:

存儲引擎MyISAM與Innodb對比圖

Mysql的另外一個索引數據結構Hash

Mysql爲何默認不用hash索引而用BTree索引的幾點原因?

Hash索引不像B-Tree 索引需要從根節點到枝節點,最後才能訪問到頁節點這樣多次的IO訪問,所以 Hash 索引的查詢效率要遠高於 B-Tree 索引,它會將計算出的Hash值和對對應的行指針信息記錄在Hash表中。但是雖然Hash效率很高但是同樣也有很多的弊端存在和限制存在。

(1)Hash 索引僅僅能滿足"=",“IN"和”<=>"查詢,不能使用範圍查詢。

(2)Hash 索引無法被用來避免數據的排序操作。

(3)Hash 索引不能利用部分索引鍵(組合索引)查詢。

(4)Hash 索引在任何時候都不能避免表掃描。

(5)Hash 索引遇到大量Hash值相等的情況後性能並不一定就會比B-Tree索引高。

因爲Hash 索引比較的是進行 Hash 運算之後的 Hash 值,所以它只能用於等值的過濾,不能用於基於範圍的過濾。經過相應的 Hash 算法處理之後的 Hash 值的大小關係,並不能保證和Hash運算前完全一樣,數據庫自然也無法利用索引的數據來避免任何排序運算。

Hash 索引在計算 Hash 值的時候是組合索引鍵合併後再一起計算 Hash 值,而不是單獨計算 Hash 值,所以通過組合索引的前面一個或幾個索引鍵進行查詢的時候,Hash 索引也無法被利用。而我們常用的業務中不可能沒有範圍查詢,所以Hash索引就不太常用咯;

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