mysql索引底層原理分析
大家都知道索引的重要性,基本用法在上章《最全面的mysql索引知識大盤點》已分享過,本章主要是探索索引的底層實現原理。當然了,我們還是以mysql為基準進行探討。
目錄
首先了解索引之前,我們先要了解個事情,innodb和myisam的區別?當然也是淺談下,
InnoDB |
MyISAM |
|
簡介 |
由Innobase Oy公司開發。 支援事務安全的引擎,支援外來鍵、行鎖、事務是他的最大特點。如果有大量的update和insert,建議使用InnoDB,特別是針對多個併發和QPS較高的情況。 |
預設表型別,它是基於傳統的ISAM型別,ISAM是Indexed Sequential Access Method (有索引的順序訪問方法) 的縮寫,它是儲存記錄和檔案的標準方法。不是事務安全的,而且不支援外來鍵,如果執行大量的select,insert MyISAM比較適合。 |
使用場景 |
線上事務處理(OLTP)型應用 |
線上分析處理(OLAP) 型應用 |
鎖差異 |
Innodb支援事務和行級鎖,是innodb的最大特色。 事務的ACID屬性,併發事務帶來的幾個問題:更新丟失,髒讀,不可重複讀,幻讀。 事務隔離級別:未提交讀(Read uncommitted),已提交讀(Read committed),可重複讀(Repeatable read),可序列化(Serializable) |
myisam只支援表級鎖,使用者在操作myisam表時,select,update,delete,insert語句都會給表自動加鎖,如果加鎖以後的表滿足insert併發的情況下,可以在表的尾部插入新的資料。也可以通過lock table命令來鎖表,這樣操作主要是可以模仿事務,但是消耗非常大,一般只在實驗演示中使用。 |
資料庫檔案差異 |
innodb屬於索引組織表 innodb有兩種儲存方式,共享表空間儲存和多表空間儲存 兩種儲存方式的表結構和myisam一樣,以表名開頭,副檔名是.frm。 如果使用共享表空間,那麼所有表的資料檔案和索引檔案都儲存在一個表空間裡,一個表空間可以有多個檔案,通過innodb_data_file_path和innodb_data_home_dir引數設定共享表空間的位置和名字,一般共享表空間的名字叫ibdata1-n。 如果使用多表空間,那麼每個表都有一個表空間檔案用於儲存每個表的資料和索引,檔名以表名開頭,以.ibd為副檔名。 |
myisam屬於堆表 myisam在磁碟儲存上有三個檔案,每個檔名以表名開頭,副檔名指出檔案型別。 .frm 用於儲存表的定義 .MYD 用於存放資料 .MYI 用於存放表索引 myisam表還支援三種不同的儲存格式: 靜態表(預設,但是注意資料末尾不能有空格,會被去掉) 動態表 壓縮表 |
索引差異 |
1、關於自動增長 myisam引擎的自動增長列必須是索引,如果是組合索引,自動增長可以不是第一列,他可以根據前面幾列進行排序後遞增。 innodb引擎的自動增長列必須是索引,如果是組合索引也必須是組合索引的第一列。 2、關於主鍵 myisam允許沒有任何索引和主鍵的表存在, myisam的索引都是儲存行的地址。 innodb引擎如果沒有設定主鍵或者非空唯一索引,就會自動生成一個6位元組的主鍵(使用者不可見) innodb的資料是主索引的一部分,附加索引儲存的是主索引的值。 3、關於count()函式 myisam儲存有表的總行數,如果select count(*) from table;會直接取出出該值 innodb沒有儲存表的總行數,如果使用select count(*) from table;就會遍歷整個表,消耗相當大,但是在加了wehre 條件後,myisam和innodb處理的方式都一樣。 4、全文索引 myisam支援 FULLTEXT型別的全文索引 innodb不支援FULLTEXT型別的全文索引,但是innodb可以使用sphinx外掛支援全文索引,並且效果更好。(sphinx 是一個開源軟體,提供多種語言的API介面,可以優化mysql的各種查詢) 5、delete from table 使用這條命令時,innodb不會從新建立表,而是一條一條的刪除資料,在innodb上如果要清空儲存有大量資料的表,最 好不要使用這個命令。(推薦使用truncate table,不過需要使用者有drop此表的許可權) 6、索引儲存位置 myisam的索引以表名+.MYI檔案分別儲存。 innodb的索引和資料一起儲存在表空間裡 |
1.物理磁碟知識
首先dbms本身就是一個檔案管理系統,只是它的實現方式確實比較複雜,但本質上是要通過訪問磁碟才能完成資料的儲存與檢索。本著刨根問底的精神,就要分析檔案是儲存及檢索的。
1.1基本概念
碟片 |
|
盤面 |
|
磁頭 |
|
磁軌 |
|
柱面 |
|
扇區 |
識別符號就是扇區頭標,包括組成扇區三維地址的三個數字:盤面號,柱面號,扇區號(塊號)。 資料段可分為資料和保護資料的糾錯碼(ECC)。 |
磁碟塊/簇 |
|
Page |
|
磁碟容量計算
儲存容量 = 磁頭數 × 磁軌(柱面)數 × 每道扇區數 × 每扇區位元組數
某磁碟是一個 3個圓盤6個磁頭,7個柱面(每個碟片7個磁軌) 的磁碟,每條磁軌有12個扇區,所以此磁碟的容量為:6 * 7 * 12 * 512 = 258048
1.2硬碟中的資料
資訊儲存在硬盤裡,硬碟是由很多的碟片組成,通過碟片表面的磁性物質來儲存資料。
把碟片放在顯微鏡下放大,可以看到碟片表面是凹凸不平的,凸起的地方被磁化,代表數字 1,凹的地方沒有被磁化,代表數字 0,因此硬碟可以通過二進位制的形式來儲存表示文字、圖片等的資訊。
所有的碟片都固定在一個旋轉軸上,這個軸即碟片主軸,所有的碟片之間是絕對平行的,在每個碟片的盤面上都有一個磁頭,磁頭與碟片之間的距離比頭髮絲的直徑還小。
所有的磁頭連在一個磁頭控制器上,由磁頭控制器負責各個磁頭的運動,磁頭可沿碟片的半徑方向移動,實際上是斜切運動,每個磁頭同一時刻必須是同軸的,即從正上方往下看,所有磁頭任何時候都是重疊的。
由於技術的發展,目前已經有多磁頭獨立技術了,在此不考慮此種情況。
碟片以每分鐘數千轉到上萬轉的速度在高速運轉,這樣磁頭就能對碟片上的指定位置進行資料的讀寫操作。
由於硬碟是高精密裝置,塵埃是其大敵,所以必須完全密封。
1.3磁碟的讀寫原理
系統將檔案儲存到磁碟上時,按柱面、磁頭、扇區的方式進行,即最先是第1磁軌的第一磁頭下的所有扇區,然後是同一柱面的下一個磁頭……
一個柱面儲存滿後就推進到下一個柱面,直到把檔案內容全部寫入磁碟。
系統也以相同的順序讀出資料,讀出資料時通過告訴磁碟控制器要讀出扇區所在柱面號、磁頭號和扇區號(實體地址的三個組成部分)進行。
注:作業系統讀取同理,只是顆粒的更大的塊操作
1.5磁碟的讀取響應時間
當需要從磁碟讀取資料的時候,系統會將資料的邏輯地址傳遞個磁碟,磁碟的控制電路按照定址邏輯將邏輯地址翻譯成實體地址,即確定要讀的資料在哪個磁軌,哪個扇區。
首先必須找到柱面,即磁頭需要移動對準相應磁軌,這個過程叫做尋道。
然後目標扇區旋轉到磁頭下,即磁碟旋轉將目標扇區旋轉到磁頭下。
尋道(時間):磁頭移動定位到指定磁軌所需要的時間,尋道時間越短,I/O操作越快,目前磁碟的平均尋道時間一般在3-15ms,一般都在10ms左右。
旋轉延遲(時間):碟片旋轉將請求資料所在扇區移至讀寫磁頭下方所需要的時間,旋轉延遲取決於磁碟轉速。普通硬碟一般都是7200rpm,慢的5400rpm。
資料傳輸(時間):資料在磁碟與記憶體之間的實際傳輸所需要的時間。
- 確定磁碟地址(柱面號,磁頭號,扇區號),記憶體地址(源/目):
- 為了讀取這個扇區的資料,需要將磁頭放到這個扇區上方,為了實現這一點:
- 即一次訪盤請求(讀 / 寫)完成過程由三個動作組成:
注:讀寫一次磁碟資訊所需的時間中軟體應著重考慮減少尋道時間和延遲時間。
1.6 I/O 的預讀原理
由於儲存介質的特性,磁碟本身存取就比主存慢很多,再加上機械運動耗費的時間,磁碟的存取速度往往是主存的幾百分之一。
因此,電腦科學中著名的區域性性原理:
-
當一個數據被用到時,其附近的資料一般來說也會被馬上使用。
-
程式執行期間所需要的資料通常比較集中。
-
由於磁碟順序讀取的效率很高(不需要尋道時間,只需要很少的旋轉時間),因此對於具有區域性性的程式來說,預讀可以提高 I/O 效率。
預讀的長度一般為頁(在許多作業系統中,頁的大小通常為 4k)的整數倍。作業系統以記憶體頁為單位管理記憶體,記憶體頁的大小對系統性能有影響。當程式要讀取的資料不在主存中時,會觸發一個缺頁異常,此時系統會向磁碟發出讀盤資訊,磁碟會找到資料的起始位置並向後連續讀取一頁或幾頁的資料載入記憶體中,然後異常返回,程式繼續執行。
2.推理並拆解普通查詢語句
select * from talbe_name where id=1
step1:找到資料檔案
step2:讀取資料檔案
step3:讀取id=1的資料
理論上是這樣的,
索引是一種用來實現高效獲取資料的資料結構,建索引的目的是為了查詢的優化,特別是當資料很龐大的時候,非常重要。一般的查詢演算法有順序查詢、折半查詢、快速查詢等,但是每種查詢演算法只能應用於特定的資料結構,例如順序查詢依賴於順序結構,折半查詢通過二叉查詢樹或紅黑樹實現二分搜尋。因此在資料之外,資料庫系統還維護著滿足特定查詢演算法的資料結構,它以某種方式引用資料。
3.為什麼要用B+Tree實現
目前大多數資料庫系統及檔案系統都採用 B-Tree 或其變種 B+Tree 作為索引結構。B+ 樹中的 B 代表平衡,而不是二叉。B+ 樹是從最早的平衡二叉樹演化而來的。B+ 樹是由二叉查詢樹、平衡二叉樹(AVLTree)和平衡多路查詢樹(B-Tree)逐步優化而來。
- 二叉查詢樹:左子樹的鍵值小於根的鍵值,右子樹的鍵值大於根的鍵值。
- AVL 樹:平衡二叉樹(AVL 樹)在符合二叉查詢樹的條件下,還滿足任何節點的兩個子樹的高度最大差為 1,但不是紅黑樹。
- 平衡多路查詢樹(B-Tree):為磁碟等外儲存裝置設計的一種平衡查詢樹。
那麼糾結該如何選型呢?索引的標準:IO漸進複雜度,說白了就是推演過程(每個節點都是1次IO)
注:B-Tree就是我們常說的B樹,一定不要讀成B減樹,否則就很丟人了
系統從磁碟讀取資料時是以磁碟塊(block)為基本單位的,位於同一磁碟塊中的資料會被一次性讀取出來,而不是按需讀取。
InnoDB 儲存引擎使用頁作為資料讀取單位,頁是其磁碟管理的最小單位,預設 page 大小是 16k。系統的一個磁碟塊的儲存空間往往沒有這麼大,因此 InnoDB 每次申請磁碟空間時都會是若干地址連續磁碟塊來達到頁的大小 16KB。
InnDB 在把磁碟資料讀入到磁碟時會以頁為基本單位,在查詢資料時如果一個頁中的每條資料都能助於定位資料記錄的位置,這將會減少磁碟 I/O 的次數,提高查詢效率。
3.1 B-Tree
B-Tree 結構的資料可以讓系統高效的找到資料所在的磁碟塊。
為了描述 B-Tree,首先定義一條資料記錄為一個二元組 [key, data],key 為記錄的鍵值,對於不同資料記錄,key 是互不相同的;data 為資料記錄除 key 外的資料。
那麼m階 B-Tree 是滿足下列條件的資料結構:
- 每個節點最多擁有m個子樹
- 根節點至少有2個子樹
- 分支節點至少擁有m/2顆子樹(除根節點和葉子節點外都是分支節點)
- 所有葉子節點都在同一層、每個節點最多可以有m-1個key,並且以升序排列
每個節點佔用一個磁碟塊,一個節點上有兩個升序排序的關鍵字和三個指向子樹根節點的指標,指標儲存的是子節點所在磁碟塊的地址。兩個關鍵詞劃分成的三個範圍域對應三個指標指向的子樹的資料的範圍域。
以根節點為例,關鍵字為 17 和 35,P1 指標指向的子樹的資料範圍為小於 17,P2 指標指向的子樹的資料範圍為 17~35,P3 指標指向的子樹的資料範圍為大於 35。模擬查詢關鍵字 29 的過程:
- 根據根節點找到磁碟塊 1,讀入記憶體。【磁碟 I/O 操作第 1 次】
- 比較關鍵字 29 在區間(17,35),找到磁碟塊 1 的指標 P2。
- 根據 P2 指標找到磁碟塊 3,讀入記憶體。【磁碟 I/O 操作第 2 次】
- 比較關鍵字 29 在區間(26,30),找到磁碟塊 3 的指標 P2。
- 根據 P2 指標找到磁碟塊 8,讀入記憶體。【磁碟 I/O 操作第 3 次】
- 在磁碟塊 8 中的關鍵字列表中找到關鍵字 29。
MySQL 的 InnoDB 儲存引擎在設計時是將根節點常駐記憶體的,因此力求達到樹的深度不超過 3,也就是說 I/O 不需要超過 3 次,分析上面過程,發現需要 3 次磁碟 I/O 操作,和 3 次記憶體查詢操作。由於記憶體中的關鍵字是一個有序表結構,可以利用二分法查詢提高效率。而 3 次磁碟 I/O 操作是影響整個 B-Tree 查詢效率的決定因素。
B-Tree 相對於 AVLTree 縮減了節點個數,使每次磁碟 I/O 取到記憶體的資料都發揮了作用,從而提高了查詢效率。
3.2 B+Tree
B+Tree 是在 B-Tree 基礎上的一種優化,InnoDB 儲存引擎就是用 B+Tree 實現其索引結構。
B-Tree主要有以下問題
- 每個節點中有key,也有data,而每一個頁的儲存空間是有限的,如果data資料較大時將會導致每個節點(即一個頁)能儲存的 key 的數量很小。
- 當儲存的資料量很大時同樣會導致 B-Tree 的深度較大,增大查詢時的磁碟 I/O 次數,進而影響查詢效率
在 B+Tree 中,所有資料記錄節點都是按照鍵值大小順序存放在同一層的葉子節點上,而非葉子節點上只儲存 key 值資訊,這樣可以大大加大每個節點儲存的 key 值數量,降低 B+Tree 的高度,變化點:
- 資料是存在葉子節點中的
- 資料節點之間是有指標指向的
通常在B+Tree上有兩個頭指標,一個指向根節點,另一個指向關鍵字最小的葉子節點,而且所有葉子節點(即資料節點)之間是一種鏈式環結構。因此可以對 B+Tree 進行兩種查詢運算:一種是對於主鍵的範圍查詢和分頁查詢,另一種是從根節點開始,進行隨機查詢。
4.Mysql索引是如何實現的
4.1 InnoDB 中的 B+Tree
InnoDB 是通過 B+Tree 結構對 ID 建索引,然後在葉子節點中儲存記錄。採用 InnoDB 引擎的資料儲存檔案有兩個,一個定義檔案,一個是資料檔案。
若建索引的欄位不是主鍵 ID,則對該欄位建索引,然後在葉子節點中儲存的是該記錄的主鍵,然後通過主鍵索引找到對應的記錄。
4.2 Myisam 中的 B+Tree
Myisam 引擎也是採用的 B+Tree 結構來作為索引結構。由於 Myisam 中的索引和資料分別存放在不同的檔案,所以在索引樹中的葉子節點中存的資料是該索引對應的資料記錄的地址,由於資料與索引不在一起,所以 Myisam 是非聚簇索引。
最後,喲沒有感覺不是那麼複雜了。