MySQL調優之索引

文檔

MySQL官網文檔:

https://dev.mysql.com/doc/refman/8.0/en/mysql-indexes.html

特截取其中一段說明:
在這裏插入圖片描述
文檔中有寫明,MySQL絕大部分的索引都是存儲在B-tree中(此處應該是B+tree),MEMORY類的存儲引擎索引使用的是hash。下面我們來認識下tree。

Tree

這邊需要簡單介紹下二叉樹BST,平衡樹AVL,紅黑樹R-B Tree,B-tree,B±tree。
推薦一個可以在線畫各種tree的網站:https://www.cs.usfca.edu/~galles/visualization/Algorithms.html

現在假設我需要依次插入1,2,3…10這10條數據:

二叉樹BST

在這裏插入圖片描述
因每一次插入的數據都是比前面的大,都成爲了前一條數據的右子樹了,而且深度非常深,如果數據庫採用BST tree作爲索引,極端情況下,會需要進行全表掃描,一層一層的往裏進行查找,顯然是不合適的。

平衡樹AVL

爲了解決二叉樹深度的問題,引入了平衡樹,要求左右兩個子樹的高度差的絕對值最大爲1:
在這裏插入圖片描述
最終tree的深度只有4,遠小於二叉樹的深度10。那MySQL爲什麼不用平衡樹作爲索引的存儲結構呢?下面我們來分析下AVL數的插入過程。
當插入數據1,2時,tree結構如下:
在這裏插入圖片描述
此時根節點001的左子樹深度爲0,右子樹深度爲1,滿足AVL樹的要求,同理節點002也是滿足的,下面插入003節點,如果我們繼續根據樹的定義,將003節點作爲002節點的右子樹,那麼此時001節點的左子樹深度還是0,但是右子樹深度就變成了2,此處已經不滿足平衡二叉樹的要求了,需要對002節點進行左旋,最終結果如下:
在這裏插入圖片描述
一樣的道理,當插入節點004還是需要進行選擇,如果後續數據庫中有幾十上百條數據,插入或者刪除一條數據之後,可能就需要對整個AVL做非常多的左旋和右旋。AVL tree雖然查詢效率會高,但是增、刪的效率會很低,也不適合用來做索引的存儲結構。

紅黑樹 R-B Tree

在這裏插入圖片描述
紅黑樹本身也是一個平衡二叉樹,對比AVL tree,紅黑樹明顯擴展了右子樹的深度,目的是爲了減少平衡樹中太多頻繁的節點左旋和右旋,但是左旋和右旋只是減少了一部分,還是不適合做索引的存儲結構。

B-Tree

B-Tree不再是二叉樹,是一種多路搜索樹。需要自定義非葉子節點最多有M個子節點,且M>2,
爲了形象點展示,就不已1-10的數字爲例了。
在這裏插入圖片描述
以3階B-Tree爲例,每一個節點,最多有3個子節點。同時,每一個節點,有三個指針,分別是p1,p2,p3,還有兩個key。以根節點爲例,兩個key分別是18,36。p1,p2,p3三個指針分別指向三個子節點。
假設現在需要查找key=12的數據:

  1. 12<18,命中磁盤塊1中指針p1,從而找到磁盤塊2
  2. 12>10 && 12<15,命中磁盤塊2中的指針p2,從而找到磁盤塊6
  3. 磁盤塊6中找到key=12的data

也就是說在3階B-tree的情況下,3次查詢就可以找到我們需要的數據,其性能比上面幾種樹結構要高很多。

相信大家都知道,一般數據庫索引,使用的都是B+tree,而不是B-Tree,那B-Tree有上面問題呢?
以MySQL的InnoDB存儲引擎爲例。OS讀取磁盤數據,是以一個磁盤塊(block)爲單位,一次性讀取出來。InnoDB使用分頁(page)的概念來管理磁盤,默認一頁是16K(可以通過配置調整爲4,8,16等)。假設上圖中一個data數據大小1k,p1,p2,p3指針不佔空間,那麼從根節點開始往下的三層(到葉子節點了),最多可以容納數據161616=4096條數據,在高併發或者DB數據量很大的情況下,需要讀取很多磁盤塊才能找到數據。

顯然問題的關鍵點就是在這個data上面,data佔據了很大的空間,由此引出了B+Tree

B+Tree

簡單理解,就是在B-Tree的基礎上,所有非葉子節點不在存儲data:
在這裏插入圖片描述
現在非葉子節點,裏面只有p1,p2,p3指針和兩個key,數據量非常小,假設佔10字節,那非葉子節點,每一頁可以讀取容量爲161000/10=1600條,三層結構下來可以查找16001600*16=40960000條數據

結構不同點

還記得在上一章講過InnoDB和MyISAM兩種存儲引擎嗎?InnoDB是聚簇索引,只有一個.ibd文件,即數據與索引是放在一起的,而MyISAM是非聚簇索引,數據與索引時兩個文件進行存儲。當我們使用索引進行檢索時,雖然InnoDB和MyISAM都是使用B+Tree進行索引,但是數據結構和過程是不一樣的。

InnoDB:
在這裏插入圖片描述
因爲索引與數據是存放在一起的,其葉子節點的數據,即爲數據庫表中記錄的真實數據

MyISAM:
在這裏插入圖片描述
因爲索引與數據文件是分開來存儲的,其葉子節點,當前key對於data,在數據表中的位置,根據這個位置,去數據表中就可以找到實際存儲的數據。

B+Tree索引種類

索引有這麼幾種:

  • 主鍵索引
  • 唯一索引
  • 普通索引
  • 全文索引
  • 組合索引

MySQL默認會對唯一約束添加索引,即當我們沒有指定索引時,數據庫會默認對 主鍵/唯一約束建立索引。如果找不到唯一約束,會默認使用6位的rowid進行索引。

回表:
當我們對非唯一約束添加索引後,其生成的B+Tree葉子節點,保存的並不是表裏面的真實數據,而是其對應的唯一約束key,通過這個唯一約束key,去查找唯一索引(或者主鍵索引)的B+Tree,它的葉子節點保存的纔是真實數據,這個過程叫做回表。
回表,只針對普通索引。

索引覆蓋:
通過上面回表的過程,可以看到需要查詢兩個索引表,會比較耗時。假設我們對非唯一鍵username添加索引,
select * from xxx where username =‘張三’,這種查詢是需要進行回表的。
select orderid from xxx where username=‘張三’,我們通過username找到唯一約束orderid時,在查找username的索引表時,其葉子節點保存的就已經是唯一約束orderid,此時就不需要再去關聯唯一索引表了,已經可以直接讀取出數據了,這種叫索引覆蓋

最左匹配:
當我們以a+b 建立組合索引之後,B+Tree的結構是什麼樣的呢?先以a項進行比較,確定下一步的查找方向,當a值一樣時,再以b的值進行比較。
如:where a=1 and b>1;這種情況是可以使用索引的,先用a=1查找到使用某一個節點,再根據b>1查找更細的節點
where b=1,這種情況無法使用索引,因缺失a的信息,數據庫不清楚應該從哪個節點開始查找,索引失效

從上面的介紹可以看出,當a一樣時,b纔是有序排列,可查找的,如:
where a >1 and b =1;這種情況下,a >1是可以命中索引的,但是b無法命中索引,因爲a>1是個範圍,在這種情況下,b是無序的。
where a=1 and b = 1;這種情況下,a=1時,b的排列是有序的,可以命中索引
在這裏插入圖片描述
組合索引:
現在對字段a,b,c建立組合索引(a,b,c),

SQL 索引情況
where a =1 使用索引,只使用a
where a =1 and b =5 使用索引,使用了a,b
where a=1 and b=5 and c=1 使用索引,使用了a,b,c
where b=5 根據最左匹配原則,索引不生效
where a =1 and c =1 使用索引,使用了a,c根據最左匹配原則,不生效
where a=1 and b>10 and c=1 使用了索引a,b
where a=1 and b like ‘1%’ and c=1 使用了索引a,b

hash索引

在MySQL中,只有Memory存儲引擎支持hash索引,但是hash索引有下面一些限制:

  1. hash索引只包含hash值和行指針,不是存儲字段值,不能使用hash中的值來避免讀取行
  2. hash索引不是按照索引順序進行排序的,無法進行排序
  3. hash索引不支持部分列匹配查找,使用的是列的全部內容來計算hash值,只能進行精確匹配
  4. 只支持等值比較,不支持任何範圍查詢
  5. 容易出現hash衝突
  6. 存儲在內存中,易丟失

優化

  1. 當使用索引列進行查詢的時候,儘量不要使用表達式,把計算邏輯放到自己代碼中去,而不是放在數據庫中執行
    在這裏插入圖片描述
  2. 儘量使用主鍵查詢,而不是其他索引,其他索引會觸發回表
  3. 使用索引掃描來排序,B+tree索引本身就是有序的。
    mySQL有兩種方式進行排序,通過排序操作或者按索引順序掃描,如果explain出來的type列的值爲index,則說明mysql使用了索引掃描來排序。
    在這裏插入圖片描述
    聯合主鍵(a,b,c)
    select xx,xx from xxx where a =‘xxx’ order by b,c;
    select xx,xx from xxx where a =‘xxx’ order by b desc;

使用a進行過濾,b、c/(b)進行排序,將他們組合在一起,正好可以可以滿足索引最左匹配原則

select xx,xx from xxx where a =‘xxx’ order by b desc , c asc;
這種無法使用索引排序,b,c一個是desc,一個是asc,使用了兩種不同的排序方向

select xx,xx from xxx where a > ‘xxx’ order by b,c;
這種也是無法使用索引排序,a>‘xxx’,是個範圍,不滿足最左匹配原則

由此我們可以得出結論,只有當索引的列順序和order by子句的順序完全一致,並且所有列的排序方式都一致時,mysql才能用索引來對結果進行排序。如果查詢需要關聯多張表,則只有當order by子句引用的字段全部爲第一張表時,才能使用索引做排序,需要滿足最左匹配要求。

  1. union all,in,or都能夠使用索引,但是推薦使用in
  2. 待完成…
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章