MySQL性能調優(1)理解底層B+tree機制

索引是誰實現的

索引是存儲引擎實現的:
本文章主要對MySQL常用的MyISAM與InnoDB這兩個存儲引擎做分析。

索引是什麼

索引是爲了加速對錶中的數據行的檢索而創建的一種分散存儲的數據結構

爲什麼要用索引

  1. 索引能極大的減少存儲引擎需要掃描的數據量。(比如全表掃描就是在找數據)
  2. 索引可以把隨機IO變成順序IO。(因爲索引是有序的這樣就能保證找數據的時候穩定性,在程序中不允許有不穩定因素。)

爲什麼MySQL要用b+tree來實現索引

在這裏先推薦一個網址來學習二叉樹由來地址

  1. 先來看看二叉查找樹 Binary Search Tree

  2. 在來看看平衡二叉查找樹(所有節點數高度不會超過1)AVL Trees (Balanced binary search trees)記住圖上的磁盤塊上存儲了數據區的磁盤地址。

  3. 說說爲什麼MySQL沒有選擇這些算法而去選擇B+Tree

    • 它太深了,數據存在的(高)深度決定着他的IO操作次數,IO操作耗時大這個大家都是知道的。
    • 他太小了,IO操作是很耗時,他一次IO也只能加載一個關鍵字;保存的東西太少了。
    • 沒有很好的利用操作磁盤IO的數據交換特性(操作系統通過硬盤讀取數據一次IO操作讀取的大小是4k(頁爲單位)) 這就是爲什麼SSD在分區時選擇4k對其的原因,能使他大大提升讀寫性能。 二叉樹每個節點只有一個關鍵字,他是存滿不了4k的,這樣會浪費資源。
    • 也沒有利用好磁盤IO的預讀能力(空間局部性原理),從而帶來頻繁的IO操作。空間局部性原理就是: 操作系統每次IO操作讀取一頁,他會有預讀能力,把下一頁或者後面幾頁的數據讀取。(注:MySQL定義的一頁爲16k)

多路平衡查找樹B-Tree

1. 上圖是一個二三樹(沒畫完 畫不下了)。2路以上爲多路。關鍵字個數爲路數-1。 2. 從IO太深了來看,他就比平衡二叉樹好的多,他一個節點就可以存儲更多的數據,這裏設置的是3路,我們可以改成更多路。一個節點存儲更多關鍵字。 3. 還可以把一個節點大小設置爲16k這樣 ``` 用id做索引 int大小 4byte+冗餘4byte 節點16k*1024byte 那麼我可以保存16*1024/8 個關鍵字 這就是爲什麼我們字段大小要設計精確,不要有太多的冗餘 ```
  1. 這裏的關鍵字多了,也就解決了空間局部性原理,讀取更多數據。

加強版多路平衡查找樹B+Tree

1. B+非葉節點不保存數據相關信息,只保存關鍵字和子節點的引用(掃庫掃表能力更強,磁盤讀寫能力更強)。 2. B+關鍵字對應的數據保存在葉子節點中(查詢更穩定)。 3. B+對排序有天然的優勢,葉子結點是順序排列(排序能力更強)。 4. 採用的是左閉合區間,這樣就是關鍵字於區間就是相同個數。

在存儲引擎MyISAM中B+Tree的體現形式

1.用單獨文件(後綴myd文件)來存放索引數據,是通過頁子節點保存數據地址

單個索引

多個索引 (對應的數據是同一條就是指向一個位置)

在存儲引擎Innodb中B+Tree的體現形式

1. Innodb中的索引是與數據存放在一起的,是以主鍵爲索引來組織數據的存儲。如果沒有主鍵,那麼他會默認生成一個int長度爲6 byte的隱藏主鍵。 2. Innodb中輔助索引,他會存放主鍵索引,找輔助數據時,他會再索引一遍主鍵索引。 3. 聚集索引只在innodb中會出現,因爲他是基於主鍵的索引,有序的。

Innodb與MyISAM對比索引流程

補充知識

列的離散性:

離散性越高、選擇性就越好(列數據差別越大,離散性越好)。

最左匹配原則:

對索引中關鍵字進行計算(對比),一定是從左往右依次進行,且不可跳過。

聯合索引
//單列索引是特殊的聯合索引
單列索引:節點中關鍵字[name]
聯合索引:節點中關鍵字[name,phoneNum]
  • 聯合索引列選擇原則
經常用的列優先 【最左匹配原則】
選擇性(離散度)高的列優先【離散度高原則】
寬度小的列優先【最少空間原則】
常用解決方案誤區
經排查發現最常用的sql語句:
Select * from users where name = ? ;
Select * from users where name = ? and phoneNum = ?;
機靈的李二狗的解決方案:
create index idx_name on users(name);×
create index idx_name_phoneNum on users(name,phoneNum);

這上面的兩種查詢:只有第二種方案才能都命中索引。

覆蓋索引
  • 如果查詢列可通過索引節點中的關鍵字直接返回,則該索引稱之爲覆蓋索引。
  • 覆蓋索引可減少數據庫IO,將隨機IO變爲順序IO,可提高查詢性能
  • 如果我們建立了一個聯合索引:name+phone
select name,phone from user where name=?;
  • 這就叫覆蓋索引。他就不需要遍歷到頁子節點,直接就返回關鍵字數據了。這也就是很多公司不讓用select* 的原因。 因爲索引的過程是一個順序的IO 全表掃描是一個隨機IO。這一點就體現了索引的效率。

理解一下

  • 索引列的數據長度能少則少。

長度決定磁盤塊關鍵字多少,一次IO操作得到的數據量。

  • 索引一定不是越多越好,越全越好,一定是建合適的。

因爲索引在增刪改的時候,數據庫都會去維護索引。

  • 匹配列前綴可用到索引 like 9999%,like %9999%、like
    %9999用不到索引;

like 9999% 他是可能用到索引可能用不到索引,因爲列離散性太差的話,數據量大的話。數據庫覺得你索引用上與沒用上效率差不多,他就不會用索引。其餘兩個最左匹配原則索引會失效。

  • Where 條件中 not in 和 <>操作無法使用索引;

因爲在索引查找中B+Tree他是根據區間查找數據,如果是不等於:他是不知道去哪個區間的。

  • 匹配範圍值,order by 也可用到索引;

MYISAM本身是具有排序功能的,還有就是索引是根據區間找數據,所以匹配範圍值可以用到索引(單個索引)。
多用指定列查詢,只返回自己想到的數據列,少用select *;

  • 聯合索引中如果不是按照索引最左列開始查找,無法使用索引;

最左匹配原則。

  • 聯合索引中精確匹配最左前列並範圍匹配另外一列可以用到索引;

最左匹配原則。精確就直接指向那條路,然後再範圍。

  • 聯合索引中如果查詢中有某個列的範圍查詢,則其右邊的所有列都無法使用索引;

因爲他畢竟是個機器 (mysql的索引機制,第一步就是要確立,搜索的順序) age>18 他可以通過關鍵的比對確認走那邊子樹,但是通過age>18 又加上name=張三這樣的條件,他無法確認你要搜索的順序。

  • 索引列少計算

索引列不能參與計算,保持列“乾淨”。比如from_unixtime(create_time) = ’2017-05-29’就不能使用到索引,原因很簡單,b+樹中存的都是數據表中的字段值,但進行檢索時,需要把所有元素都應用函數才能比較,顯然成本太大。
爲了解決索引列上計算引起的索引失效問題,將計算放到索引列外的表達式上,所以語句應該寫成create_time = unix_timestamp(’2017-05-29’)

來一首打油詩

全值匹配我最愛,最左前綴要遵守;
帶頭大哥不能死,中間兄弟不能斷;
索引列上少計算,範圍之後全失效;
LIKE百分寫最右,覆蓋索引不寫星;
不等空值還有or,索引失效要少用;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章