==> 學習彙總(持續更新)
==> 從零搭建後端基礎設施系列(一)-- 背景介紹
摘要:爲什麼要寫這篇文章?因爲從網上搜索"索引失效的原因"時,要麼是一些片面的總結性用語,例如"如果條件中有or,即使其中有條件帶索引也不會使用",要麼就是對着一些例子搭配explain進行籠統的解釋。導致我經常看過就忘,究其根本原因就是沒理解透徹,所以我經過從官方文檔、博客和書籍等,來探究其內部索引使用的原理。鑑於內容較多,分爲上下兩篇進行分析,上篇主要了解索引原理以及查詢優化器的概念,下篇根據上篇學到的知識並結合查詢優化器,對於各種索引失效場景進行分析以及SQL調優思想總結。另外,會有一篇專門分析查詢優化器的文章。
一、索引是個什麼東西?
- 其實我覺得字典、圖書館的類比已經挺形象的了。就拿字典來說,如果你想查"hello"這個單詞,是不是第一反應是先去目錄那找到"H"開頭的單詞,接着在"H"下面繼續跳到第二個字母是"E"的單詞,依次類推,跳幾次就能很快定位到這個單詞了。但是跟這個單詞相關聯的僅僅是一個頁碼N,所以我們接着跳到第N頁,又發現這一頁有很多單詞,這時候我們只能依次遍歷這一頁,才能找到"hello"這個單詞的詳細釋義。如下圖所示
如果沒有索引,我們如何找這個單詞?很容易想象,在一本厚厚的字典裏,一頁一頁的遍歷查找,雖然有序的話,還是可以跳頁,但是需要過濾的信息量太大。 - 回到數據庫中,其實思想也是一樣的,相較於全表掃描,那通過索引定位是不是要快很多?所以數據庫中才會造出那些個索引出來。
二、MYSQL中的B+樹索引類型分類和原理分析
mysql中用的最多的就是B+樹索引,那這些索引如何分類呢?大家肯定已經知道非常多的索引類型名詞,眼花繚亂,羣魔亂舞。其實吧,mysql中的索引其實就分爲2大類分別是聚集索引和輔助索引,其它都是按照功能分類造的名詞,不需要強行去背它,只要理解了這兩類索引的區別即可。最後還有稍微特殊一點的聯合索引會單獨說一下。
-
MYSQL中的B+樹是什麼樣的
先看一個非常簡單的圖
從圖中可以得出以下幾個結論
a.只有葉子節點稱爲數據節點,並且它存儲了完整的數據
b.目錄節點只存放能夠找到下層目錄節點和數據節點的信息
c.不管是目錄節點還是數據節點,同一層次的節點用雙鏈表相互連接
d.不管是目錄節點還是數據節點,內部都用單鏈表連接
以上4個結論還比較表面,繼續看一張比較詳細的圖
從圖中可以得出以下幾個結論(假設圖中數據節點的記錄分別是id和name兩個字段)
a.所有的節點都是一個數據頁
b.目錄節點存放的記錄信息只有key
和對應的數據頁
c.遍歷數據節點能夠得到按索引字段有序的記錄
d.數據頁內並不是直接依次遍歷得到記錄,而是通過頁目錄進行二分查找
mysql中的B+樹就長這樣了,這也爲什麼說索引即數據,指的就是索引(聚集索引和輔助索引結構都長這樣,只是節點中的記錄不一樣。)中的每一個節點都是數據頁。還有一點需要注意,mysql搜索的時候是按頁搜索,定位到頁之後纔會將頁中的數據加載到內存進行查找。因爲頁中的記錄是通過二分查找+記錄組內遍歷,速度非常快,耗時忽略不計。 -
聚集索引
那什麼是聚集索引?聚集索引的數據節點存放的是完整的記錄。假設一個表有id
、name
和age
字段,爲了方便畫圖,name
假設只有單個字母a~z
。如下圖所示
從圖中又可以得到幾個結論
a.聚集索引的數據節點按照主鍵id
從左到右依次遞增。
b.聚集索引的數據節點存放了完整的記錄。
c.聚集索引的數據節點除了主鍵id
是有序的,其它字段都是無序的。 -
輔助索引
那什麼是輔助索引?可以想象一下,如果name
這一列沒有建立索引,根據name
查找的時候,是不是得去遍歷聚集索引下面的所有數據節點中的記錄一一比較呢?那能有什麼更快速的方法嗎?答案就是爲name
建立一個索引,通過name
定位到主鍵id
,然後回到聚集索引中進行查找,這樣速度就非常快。那可能有人會想問,爲什麼不直接爲name
建立一個聚集索引,這樣就能直接在數據節點找到完整的記錄信息了。是可以這麼做,但是會增加空間消耗,重複存儲冗餘數據了。所以起名爲輔助索引顧名思義,這個索引只起到一個輔助作用,真正查找數據還得回到聚集索引中找。如下圖所示,爲name
建立一個輔助索引。
從圖中又可以得到幾個結論
a.數據節點中,從左到右,按照name
從小到大排序。
b.數據節點中,只有name
和主鍵id
兩列數據。
c.主鍵id
在不同name
中是亂序的。
例如查找name=’a‘
的時候,會找到三條數據,對應的主鍵id
是1、22、30。然後回到聚集索引中,根據主鍵一個個查找,一共要進行三次回表操作。
-
聯合索引
聯合索引也是輔助索引的一種,接下來以name
和age
建立一個聯合索引。
從圖中又可以得到幾個結論
a.聯合索引中,數據節點從左到右依次按照第一個字段從小到大排序。
b.聯合索引中,數據節點中,第一個字段值相同,第二個字段按照從小到大排序。
c.聯合索引中,能夠單獨使用第一個字段作爲索引查詢,但是不能單獨使用第二個字段作爲索引查詢。 -
Q&A
-
什麼是頁目錄?
先看一下<MYSQL技內幕:InnoDB存儲引擎>這本書中的介紹用大白話解釋就是,一個數據頁內會有很多記錄,並且都是用單鏈表來連接,假設有1000條記錄,如果沒有頁目錄,最壞情況下,需要遍歷1000次到最後一條記錄。所以就想到了一種用少量空間換時間的辦法,將1000條記錄分成100組,每組記錄數10條,然後用一個數組,將每一組的第一個地址偏移量存到數組中,當進行查找的時候,使用二分查找,定位到最後一組記錄,再進行遍歷,這樣只需要遍歷27次就能找到數據,時間上是不是大大減少了呢?用圖來表示就像這樣
例如現在要查找id=11
,步驟是通過頁目錄二分查找,定位到0x20(9 < 11 < 13),然後從id=9
依次遍歷三次得到id=11
這個結果。
再舉個不是數字的例子
可以看到,頁目錄中存的是地址偏移,二分查找的時候,通過地址偏移拿到實際數據進行比較。 -
B+樹的查找過程?
當初作爲小白的我,天真的以爲是將整顆B+樹加載到內存中進行查找,現在才明白,其實是先在磁盤中查找到相應的頁,然後纔將這些頁加載到內存中進行查找。可以細品mysql書籍裏面經常說到多少多少次磁盤I/O查找,這不就是明着說B+樹的查找過程是先在磁盤查找的嗎?
-
三、什麼是查詢優化器?
那什麼是查詢優化器?那我們得先了解MYSQL的查詢流程,從客戶端提交到服務端執行都經歷什麼?
可以看到,提交引擎查詢之前,會有一個查詢優化的過程,在這一步,mysql真的是做了很多工作,可謂是絞盡腦汁幫用戶"愚蠢的行爲"買單。我們都知道explain這個關鍵字,可以幫助我們知道某條SQL語句的執行計劃。而這個執行計劃就是查詢優化器經過優化後,最終選擇的它認爲的最優的執行計劃。因爲查詢優化器內容實在太多,如果不細講,會影響下篇分析索引失效的本質,所以在本篇最後,先將執行執行計劃的各個參數含義列一遍,然後會專門寫一篇查詢優化器的原理分析。
官方文檔裏面的簡單解釋
四、總結
- 索引就像是一本字典的目錄,能快速幫你定位到需要查找的數據,付出的代價就是字典的前面會多出幾頁甚至幾十頁的目錄出來。
- 爲什麼需要索引?爲了快速定位數據唄。
- MYSQL B+樹中的索引有哪些?聚集索引和輔助索引。
- B+樹索引由目錄節點和數據節點構成,這兩種節點都稱爲數據頁,因爲它們使用的結構是一樣的,只是存放的數據不同。
- 數據頁是由頁目錄和記錄構成的,數據頁中會有成百上千條記錄,爲了快速定位查找,犧牲了一些空間,造出了頁目錄,其實就是一個數組,然後將記錄分成N組,將每組的偏移量存入數組,就能通過二分查找+記錄組內遍歷進行快速定位了。
- MYSQL查找的時候,會先從磁盤定位到需要用到的頁,然後才加載到內存進行查找。
- 查詢優化器其實就是一個愛管閒事的玩意,不管你怎麼造,它都會盡可能的幫你糾正,少走彎路。所以當你使用explain的時候,會發現很多奇奇怪怪的現象,和自己想的完全不一樣。這時候就只有深入去探究它到底是如何進行優化的,才能掌握explain真正的威力。
- 這裏推薦一下官方文檔、掘金小冊中的從根兒上理解 MySQL和MYSQL技內幕:InnoDB存儲引擎。我之前是先看的InnoDB存儲引擎,但是比較難看懂,因爲很多官話,然後遇到從根兒上理解 MySQL,作者用通俗易懂的語言,將書上和官方文檔上的一些內容講解出來,確實是不錯。但是最全的還得是官方文檔,這個是最難看懂的,但卻是最權威的,所以我會將一些疑惑點,或者書上,其它作者沒有講清楚的點,自己去官方文檔上找出來研究。這樣下來,吸收日月精華,還有誰能阻擋?哈哈。再說一些額外的話,對於那些書籍、博客等講解的知識,自己但凡有一點疑惑,都要自己去權威的官方文檔中驗證,當然了,如果你能看懂源碼,這纔是最權威的,但是太難了,只能退而求其次去官方文檔中找答案了。