MySQL查詢性能優化前,必須先掌握MySQL索引理論

越努力,越幸運,
本文已收藏在GitHub中 JavaCommunity, 裏面有面試分享、源碼分析系列文章,歡迎收藏,點贊
https://github.com/Ccww-lx/JavaCommunity

數據庫索引在平時的工做是必備的,怎麼建索引,怎麼使用索引,能夠提升數據的查詢效率。並且在面試過程,數據庫的索引也是必問的知識點,好比:git

  • 索引底層結構選型,那爲何選擇B+樹?
  • 不一樣存儲引擎的索引的體現形式有哪些?
  • 索引的類型github

    • 組合索引存儲方式
    • 查詢方式
    • 最左前綴匹配原則
  • 覆蓋索引是什麼?

看着這些,能說出多少,理解多少呢?所以咱們須要去探究其內在原理。面試

那索引是什麼?

索引的目的爲了加速檢索數據而設計的一種分散存儲(索引經常很大,屬於硬盤級的東西,因此是分散存儲)的數據結構,其原理以空間換時間。

而快速檢索的實現的本質是數據結構,經過不一樣數據結構的選擇,實現各類數據快速檢索,索引有哈希索引和B+樹索引。算法

索引底層結構選型,那爲何選擇B+樹?

數據庫索引底層選型歸根到底就是爲提升檢索效率,那麼就須要考慮幾個問題:sql

  • 算法時間複雜度
  • 是否存在排序
  • 磁盤IO與預讀
NOTE: 考慮到磁盤IO是很是高昂的操做,計算機操做系統作了一些優化,當一次IO時,不光把當前磁盤地址的數據,而是把相鄰的數據也都讀取到內存緩衝區內,由於局部預讀性原理告訴咱們,當計算機訪問一個地址的數據的時候,與其相鄰的數據也會很快被訪問到。每一次IO讀取的數據咱們稱之爲一頁(page)。

哈希表( Hash Table,散列表 )

哈希表是根據鍵(Key)而直接訪問在內存存儲位置的數據結構。數據庫

image.png

經過計算一個關於鍵值的函數,將所需查詢的數據映射到表中一個位置來訪問記錄,這加快了查找速度。雖然查詢時間複雜度爲O(1),但存在着碰撞問題,最壞狀況會致使時間複雜急劇增長;微信

並且哈希表其只適合精準key(等於)檢索,不適合範圍式檢索,範圍檢索就須要一次把全部數據找出來加載到內存,沒有效率,所以不適合Mysql的底層索引的數據結構。數據結構

普通的二叉查找樹

爲了優化高效範圍查詢,且時間複雜度小,引入二叉查找樹函數

image.png

二叉查找樹的時間複雜度是 O(lgn),因爲數據已排序好了,因此範圍查詢是能夠高效查詢,源碼分析

但會存在的問題:左右子節點的深度可能相差很大,最極端的狀況只有左子樹或者右子樹,此時查找的效率爲O(n),檢索性能急劇降低,所以也不適合Mysql的底層索引的數據結構。

image.png

平衡二叉樹(AVL樹)

爲了優化二叉樹左右子樹深度相差太大的問題,咱們引入了平衡二叉樹,即左右子節點的深度差不超過1
平衡二叉樹看來好像適合,能夠實現:

  • 能夠實現範圍查找、數據排序
  • 查詢性能良好O(logn)

image.png

NOTE:上圖中一個磁盤塊,表明硬盤上的一個存儲位置

可是咱們還有一個最重要因素須要考慮,磁盤IO與預讀,且數據庫查詢數據的瓶頸在於磁盤 IO,使用平衡二叉樹根據索引進行查找時,每讀一個磁盤塊就進行一次IO,這樣沒有實現計算機的預讀,致使檢索效率,總結出平衡二叉樹做爲索引的問題(上圖中一個磁盤塊,表明硬盤上的一個存儲位置):

  • 太深了(即它只有二條路),深度越大進行的IO操做也就越多
  • 過小了,每一次IO才查詢磁盤塊這麼一點數據,太浪費IO了。操做系統規定一次IO最小4K,Mysql一次IO 16K,而圖上的磁盤塊能明顯達不到4K

B+樹

爲了優化磁盤IO和預讀,減小IO操做,條路太少了,那麼換成多條路,那麼會想到使用B樹B+樹,但B樹每一個節點限制最多存儲兩個 key,也會形成IO操做過於頻繁,所以優化思路爲:儘量在一次磁盤 IO 中多讀一點數據到內存,那麼B+樹也該出場:

  • B+樹一個節點能存不少索引,且只有B+樹葉子節點存儲數據
  • 相鄰節點之間有一些前驅後繼關係
  • 葉子節點是順序排列的
    image.png

相對於B樹,B+樹的優點有

  • B+樹掃庫掃表的能力更強

    • B樹的數據是存放在每個節點中的,節點所在的物理地址又是隨機的,因此掃表的話,進行的是隨機IO
    • B+樹的數據是存放在葉子節點的,且在一個葉子節點中的數據是連續的,因此掃表的話,進行的相對的順序IO
  • B+樹的磁盤讀寫能力更強,枝節點不保存數據,而保存更多的關鍵字。一次IO就能讀出更多的關鍵字
  • B+樹的排序能力更強,B+樹的葉子節點存儲的數據是已經排好序的

索引的體現形式

索引在不一樣的存儲引擎中體現形式步同樣, 最多見的是:

  • Innodb 引擎中體現爲彙集索引方式 (索引和數據是存放在同一個文件的)
  • Myisam引擎中體現爲非彙集索引方式 (索引和數據是存放在兩個文件中的)

彙集索引方式(InnoDB存儲引擎)

InnoDB存儲引擎中,索引和數據是存放在同一個文件的,屬於彙集索引 。並且InnoDB會自動創建好主鍵 ID 索引樹, 所以建表時要求必須指定主鍵的緣由。

其中,主鍵索引(彙集索引)的葉子節點記錄了數據,而不是數據的物理地址。輔助索引的葉子節點存放的是主鍵key。因此當利用輔助索引查找數據時,實際上查了兩遍索引(輔助索引和主鍵索引):

  • 先查詢輔助索引樹找出主鍵
  • 而後在主鍵索引樹中根據主鍵查詢數據

image.png

非彙集索引方式(Myisam存儲引擎)

Myisam存儲引擎中,索引和數據是存放在兩個文件中的,屬於非彙集索引 。不論是主鍵索引仍是輔助索引,其葉子節點都是記錄了數據的物理地址。

image.png

MySQL的索引類型

MySQL索引能夠分爲:

  • 普通索引(index): 加速查找
  • 惟一索引:

    • 主鍵索引:primary key :加速查找+約束(不爲空且惟一)
    • 惟一索引:unique:加速查找+約束 (惟一)
  • 聯合索引:

    • primary key(id,name):聯合主鍵索引
    • unique(id,name):聯合惟一索引
    • index(id,name):聯合普通索引
  • 全文索引full text :用於搜索很長一篇文章的時候,效果最好。

其中,主要理解一下聯合索引的問題,存儲結構,查詢方式。

聯合索引

聯合索引,多個列組成的索引叫作聯合索引,單列索引是特殊的聯合索引。其存儲結構以下:

<font color='red'>對於聯合索引來講其存儲結構只不過比單值索引多了幾列,組合索引列數據都記錄在索引樹上,(不一樣的組合索引,B+樹也是不一樣的),且存儲引擎會首先根據第一個索引列排序後,其餘列再依次將相等值的進行排序。</font>

image.png

NOTE:葉節點第一排,按順序排序好,第二列,會基於第一列排序好的,將第一列相等的再下一列再排序,依次類推。

<font color='red'>聯合索引查詢方式,存儲引擎首先從根節點(通常常駐內存)開始查找,而後再依次在其餘列中查詢,直到找到該索引下的data元素即ID值,再從主鍵索引樹上找到最終數據。</font>

並且聯合索引其選擇的原則:

  • 最左前綴匹配原則(常用的列優先)
  • 離散度高的列優先
  • 寬度小的列優先
最左前綴匹配原則

最左前綴匹配原則和聯合索引的索引構建方式及存儲結構是有關係的。根據上述理解分析,能夠得出聯合索引只能從多列索引的第一列開始查找索引纔會生效,好比:

假設表user上有個聯合索引(a,b,c),那麼 select * from user where b = 1 and c = 2將不會命中索引

緣由是聯合索引的是存儲引擎先按第一個字段排序,再按第二個字段排序,依次排序。

離散度

當索引中的一列離散度太低時,優化器可能直接不走索引,離散度計算方法:

離散度 = 列中不重複的數據量 / 這一列的總數據量

覆蓋索引

若是一個索引包含(或覆蓋)全部須要查詢的字段的值,稱爲覆蓋索,即只需掃描索引而無須回表查詢 。覆蓋索引可減小數據庫IO,將隨機IO變爲順序IO,可提升查詢性能。

對於InnoDB輔助索引在葉子節點中保存了行的主鍵值,因此若是輔助索引(包括聯合索引)可以覆蓋查詢,則能夠避免對主鍵索引的二次查詢。好比:

--建立聯合索引
create index name_phone_idx on user(name,phoneNum);
--此時是覆蓋索引,緣由是根據name來查,命中索引name_phone_idx,
--其關鍵字爲name,phoneNum,自己就已經包含了查詢的列。
select name,phoneNum where name = "張三";  
--若是id爲主鍵的話,此時也稱做覆蓋索引,緣由:輔助索引的葉子節點存的就是主鍵
select id,name,phoneNum where name = "張三";

總結

MySQL的索引有不少知識點要掌握,已學習了索引的底層存儲結構,不一樣存儲引擎中的索引體現,以及索引類型的基礎原理知識分析,能夠爲後續的數據庫優化提供理論知識的支撐,也會更好的理解優化方案。後續會有優化篇章

謝謝各位點贊,沒點讚的點個贊支持支持 最後,微信搜《Ccww技術博客》觀看更多文章,也歡迎關注一波。
相關文章
相關標籤/搜索