索引是最多見的慢查詢優化方式
其是一種優化查詢的數據結構,MySql中的索引是用B+樹實現,而B+樹就是一種數據結構,能夠優化查詢速度,能夠利用索引快速查找數據,優化查詢。算法
能夠提升查詢速度的數據結構:
哈希表、徹底平衡二叉樹、B樹、B+樹等等。
哈希:select* from sanguo where name>'周瑜 哈希表的特色是能夠快速的精確查詢,可是不支持範圍查詢。
徹底平衡二叉樹:對於數據量大狀況,它相比於哈希或者B樹、B+樹須要查找次數更多。
B樹:比徹底平衡二叉樹要矮,查詢速度更快,所需索引空間更小。
B+樹:B+樹比B樹要胖,B+樹的非葉子節點會冗餘一份在葉子節點中,而且也在葉子節點會用指針相連。
B樹相比徹底平衡二叉樹查詢次數更少,即有更少的磁盤IO次數,性能更優;
B+樹是B樹的升級版,其爲了提升範圍查找的效率。
sql
總結:Mysql選用B+樹這種數據結構做爲索引,能夠提升查詢索引時的磁盤IO效率,而且能夠提升範圍查詢的效率,而且B+樹裏的元素也是有序的。數組
索引的最左前綴原則:當創建多個字段聯合索引時,如(a,b,c) 查詢條件只會走三類索引 即 a 、 ab 、 abc, ac也走,可是隻走a索引。數據結構
假若有這麼一張表(表名:sanguo):性能
如今對 name 字段創建哈希索引:優化
注意字段值所對應的數組下標是哈希算法隨機算出來的,因此可能出現哈希衝突。那麼對於這樣一個索引結構,如今來執行下面的sql語句: spa
select * from sanguo where name='周瑜'
能夠直接對‘周瑜’按哈希算法算出來一個數組下標,而後能夠直接從數據中取出數據並拿到所對應那一行數據的地址,進而查詢那一行數據。 那麼若是如今執行下面的sql語句:操作系統
select * from sanguo where name>'周瑜'
則無能爲力,由於哈希表的特色就是能夠快速的精確查詢,可是不支持範圍查詢。指針
仍是上面的表數據用徹底平衡二叉樹表示以下圖(爲了簡單,數據對應的地址就不畫在圖中了。):code
圖中的每個節點實際上應該有四部分:
左指針,指向左子樹
鍵值
鍵值所對應的數據的存儲地址
右指針,指向右子樹
另外須要提醒的是,二叉樹是有順序的,簡單的說就是「左邊的小於右邊的」假如咱們如今來查找‘周瑜’,須要找2次(第一次曹操,第二次周瑜),比哈希表要多一次。
並且因爲徹底平衡二叉樹是有序的,因此也是支持範圍查找的。
仍是上面的表數據用B樹表示以下圖(爲了簡單,數據對應的地址就不畫在圖中了。):
能夠發現一樣的元素,B樹的表示要比徹底平衡二叉樹要「矮」,緣由在於B樹中的一個節點能夠存儲多個元素。
仍是上面的表數據用B+樹表示以下圖(爲了簡單,數據對應的地址就不畫在圖中了。):
咱們能夠發現一樣的元素,B+樹的表示要比B樹要「胖」,緣由在於B+樹中的非葉子節點會冗餘一份在葉子節點中,而且葉子節點之間用指針相連。
這裏咱們用「反證法」,假如咱們如今就用徹底平衡二叉樹做爲索引的數據結構,咱們來看一下有什麼不妥的地方。實際上,索引也是很「大」的,由於索引也是存儲元素的,咱們的一個表的數據行數越多,那麼對應的索引文件其實也是會很大的,實際上也是須要存儲在磁盤中的,而不能所有都放在內存中,因此咱們在考慮選用哪一種數據結構時,咱們能夠換一個角度思考,哪一個數據結構更適合從磁盤中讀取數據,或者哪一個數據結構可以提升磁盤的IO效率。回頭看一下徹底平衡二叉樹,當咱們須要查詢「張飛」時,須要如下步驟:
從磁盤中取出「曹操」到內存,CPU從內存取出數據進行比較,「張飛」<「曹操」,取左子樹(產生了一次磁盤IO)
從磁盤中取出「周瑜」到內存,CPU從內存取出數據進行比較,「張飛」>「周瑜」,取右子樹(產生了一次磁盤IO)
從磁盤中取出「孫權」到內存,CPU從內存取出數據進行比較,「張飛」>「孫權」,取右子樹(產生了一次磁盤IO)
從磁盤中取出「黃忠」到內存,CPU從內存取出數據進行比較,「張飛」=「張飛」,找到結果(產生了一次磁盤IO)
從上面能夠發現:徹底平衡二叉樹,當咱們須要查詢「張飛」時須要4次磁盤IO,也就是4次從磁盤中取出數據(磁盤塊)到內存
同理,回頭看一下B樹,咱們發現只發送三次磁盤IO就能夠找到「張飛」了,這就是B樹的優勢:一個節點能夠存儲多個元素,相對於徹底平衡二叉樹因此整棵樹的高度就下降了,磁盤IO效率提升了。
而B+樹是B樹的升級版,只是把非葉子節點冗餘一下,這麼作的好處是爲了提升範圍查找的效率。
Mysql中MyISAM和innodb 的B+樹結構以下圖:
一般咱們認爲B+樹的非葉子節點不存儲數據,只有葉子節點才存儲數據;而B樹的非葉子和葉子節點都會存儲數據,會致使非葉子節點存儲的索引值會更少,樹的高度相對會比B+樹高,平均的I/O效率會比較低,因此使用B+樹做爲索引的數據結構,再加上B+樹的葉子節點之間會有指針相連,也方便進行範圍查找。上圖的data區域兩個存儲引擎會有不一樣。
其實也能夠換個角度來思考B+樹中一個節點到底多大合適?
答案是:B+樹中一個節點爲一頁或頁的倍數最爲合適。由於若是一個節點的大小小於1頁,那麼讀取這個節點的時候其實也會讀出1頁,形成資源的浪費;若是一個節點的大小大於1頁,好比1.2頁,那麼讀取這個節點的時候會讀出2頁,也會形成資源的浪費;因此爲了避免形成浪費,因此最後把一個節點的大小控制在1頁、2頁、3頁、4頁等倍數頁大小最爲合適。
那麼,Mysql中B+樹的一個節點大小爲多大呢?
這個問題的答案是「1頁」,這裏說的「頁」是Mysql自定義的單位(其實和操做系統相似),Mysql的Innodb引擎中一頁的默認大小是16k(若是操做系統中一頁大小是4k,那麼Mysql中1頁=操做系統中4頁),可使用命令SHOW GLOBAL STATUS like 'Innodb_page_size'; 查看。
對着上面Mysql中Innodb中對B+樹的實際應用(主要看主鍵索引),能夠發現B+樹中的一個節點存儲的內容是:
非葉子節點:主鍵+指針
葉子節點:數據
那麼,假設咱們一行數據大小爲1K,那麼一頁就能存16條數據,也就是一個葉子節點能存16條數據;再看非葉子節點,假設主鍵ID爲bigint類型,那麼長度爲8B,指針大小在Innodb源碼中爲6B,一共就是14B,那麼一頁裏就能夠存儲16K/14=1170個(主鍵+指針),那麼一顆高度爲2的B+樹能存儲的數據爲:117016=18720條,一顆高度爲3的B+樹能存儲的數據爲:11701170*16=21902400(千萬級條)。因此在InnoDB中B+樹高度通常爲1-3層,它就能知足千萬級的數據存儲。在查找數據時一次頁的查找表明一次IO,因此經過主鍵索引查詢一般只須要1-3次IO操做便可查找到數據。因此也就回答了咱們的問題,1頁=16k這麼設置是比較合適的,是適用大多數的企業的,固然這個值是能夠修改的,因此也能根據業務的時間狀況進行調整。