數據庫存儲數據結構mysql
樹型數據結構是數據庫的首選,樹具有快速查找的特性,很是適合用於數據庫這種對查詢速度要求高的的系統,針對數據庫,有幾種比較合適的結構,二叉查找樹,B樹,B+樹,B*樹;sql
如上能夠得出結論:B樹遍歷不是很方便,B+樹數據遍歷方便,查找快速,B*樹有點複雜。因此B+樹是最佳選擇。數據庫
如圖:數據結構
B/B+/B*三種樹有類似的操做,好比檢索/插入/刪除節點。這裏只重點關注插入節點的狀況,且只分析他們在當前節點已滿狀況下的插入操做,由於這個動做稍微複雜且能充分體現幾種樹的差別。與之對比的是檢索節點比較容易實現,而刪除節點只要完成與插入相反的過程便可(在實際應用中刪除並非插入的徹底逆操做,每每只刪除數據而保留下空間爲後續使用)。測試
插入過程:優化
B樹分裂:spa
下圖的紅色值即爲每次新插入的節點。每當一個節點滿後,就須要發生分裂(分裂是一個遞歸過程,參考下面7的插入致使了兩層分裂),因爲B樹的非葉子節點一樣保存了鍵值,因此已滿節點分裂後的值將分佈在三個地方:1原節點,2原節點的父節點,3原節點的新建兄弟節點(參考5,7的插入過程)。分裂有可能致使樹的高度增長(參考3,7的插入過程),也可能不影響樹的高度(參考5,6的插入過程)。 指針
B+樹的分裂:blog
當一個結點滿時,分配一個新的結點,並將原結點中1/2的數據複製到新結點,最後在父結點中增長新結點的指針;B+樹的分裂隻影響原結點和父結點,而不會影響兄弟結點,因此它不須要指向兄弟節點的指針。遞歸
B*樹的分裂:
當一個結點滿時,若是它的下一個兄弟結點未滿,那麼將一部分數據移到兄弟結點中,再在原結點插入關鍵字,最後修改父結點中兄弟結點的關鍵字(由於兄弟結點的關鍵字範圍改變了)。若是兄弟也滿了,則在原結點與兄弟結點之間增長新結點,並各複製1/3的數據到新結點,最後在父結點增長新結點的指針。能夠看到B*樹的分裂很是巧妙,由於B*樹要保證分裂後的節點還要2/3滿,若是採用B+樹的方法,只是簡單的將已滿的節點一分爲二,會致使每一個節點只有1/2滿,這不知足B*樹的要求了。因此B*樹採起的策略是在本節點滿後,繼續插入兄弟節點(這也是爲何B*樹須要在非葉子節點加一個兄弟間的鏈表),直到把兄弟節點也塞滿,而後拉上兄弟節點一塊兒湊份子,本身和兄弟節點各出資1/3成立新節點,這樣的結果是3個節點恰好是2/3滿,達到B*樹的要求,皆大歡喜。
B+樹適合做爲數據庫的基礎結構,徹底是由於計算機的內存-機械硬盤兩層存儲結構。內存能夠完成快速的隨機訪問(隨機訪問即給出任意一個地址,要求返回這個地址存儲的數據)可是容量較小。而硬盤的隨機訪問要通過機械動做(1磁頭移動 2盤片轉動),訪問效率比內存低幾個數量級,可是硬盤容量較大。典型的數據庫容量大大超過可用內存大小,這就決定了在B+樹中檢索一條數據極可能要藉助幾回磁盤IO操做來完成。以下圖所示:一般向下讀取一個節點的動做可能會是一次磁盤IO操做,不過非葉節點一般會在初始階段載入內存以加快訪問速度。同時爲提升在節點間橫向遍歷速度,真實數據庫中可能會將圖中藍色的CPU計算/內存讀取優化成二叉搜索樹(InnoDB中的page directory機制)。
真實數據庫中的B+樹應該是很是扁平的,能夠經過向表中順序插入足夠數據的方式來驗證InnoDB中的B+樹到底有多扁平。咱們經過以下圖的CREATE語句創建一個只有簡單字段的測試表,而後不斷添加數據來填充這個表。經過下圖的統計數據(來源見參考文獻1)能夠分析出幾個直觀的結論,這幾個結論宏觀的展示了數據庫裏B+樹的尺度。
1 .每一個葉子節點存儲了468行數據,每一個非葉子節點存儲了大約1200個鍵值,這是一棵平衡的1200路搜索樹!(葉子節點存儲數據大小爲硬盤一個磁盤塊大小)
2 .對於一個22.1G容量的表,也只須要高度爲3的B+樹就能存儲了,這個容量大概能知足不少應用的須要了。若是把高度增大到4,則B+樹的存儲容量馬上增大到25.9T之巨!
3 .對於一個22.1G容量的表,B+樹的高度是3,若是要把非葉節點所有加載到內存也只須要少於18.8M的內存(如何得出的這個結論?由於對於高度爲2的樹,1203個葉子節點也只須要18.8M空間,而22.1G從良表的高度是3,非葉節點1204個。同時咱們假設葉子節點的尺寸是大於非葉節點的,由於葉子節點存儲了行數據而非葉節點只有鍵和少許數據。),只使用如此少的內存就能夠保證只須要一次磁盤IO操做就檢索出所需的數據,效率是很是之高的。
索引
聚簇索引:數據和主索引存放到一塊兒,同一個文件中。
非聚簇索引:數據和主索引放在不一樣的地方。
從上圖能夠分析得出:InnoDB引擎主索引和數據是存放在同一個文件當中,當創建輔助索引,經過輔助索引來查詢數據時,會首先經過輔助索引查詢到主索引(輔助索引B+樹葉子節點存儲的是主索引號),再經過主索引樹找到對應的數據。而MyISAM引擎,主索引和輔助索引對應的B+樹和具體數據時分離的,用主索引和輔助索引查詢數據的過程是同樣的。綜上,InnoDB在插入數據方面,若是主鍵是有序值,則直接添加數據到最後,若是主鍵是無序的,則須要查詢主鍵樹,尋找合適位置插入。而MyISAM只須要直接將數據添加到最後,而後使用指針指向它便可,索引MyISAM插入效率比InnoDB高,但InnoDB查詢效率比MyISAM高。
最重要的一點是MyISAM不支持事務。
總結:
mysql中的索引是在存儲引擎中實現的 ,mysql有不少存儲引擎 可是大部分都採用B+tree做爲索引結構的 其中包括myisam和innodb。
myisam索引文件和數據文件是分離的,myisam索引的存儲方式是非聚合的,索引文件存儲在MYI文件。
innodb 索引和數據文件是保存在一塊兒的;數據共享的話會放在ibdata,獨享的話會放在ibd
innodb每一個表只有一個彙集索引。若是木有主鍵,則會選擇一個非空惟一索引來代替主鍵;若是再不存在則會定義一個隱藏的主鍵進行彙集。
所謂彙集和非彙集:非彙集索引葉子頁包含一個指向表中的記錄的指針地址,記錄的物理順序和索引的順序不一致;彙集索引則數據行和鍵值一塊兒保存在葉子頁 並且記錄的排列順序與索引的排列順序一致。