mysql explain详解_数据库mysql(1)——B+TREE索引原理
一、B+Tree索引詳解
1.什么是索引?
索引:加速查詢的數據結構。
2.索引常見數據結構:
#1.順序查找: 最基本的查詢算法-復雜度O(n),大數據量此算法效率糟糕。
#2.二叉樹查找(binary tree search): O(log2n)
左邊是數據表,一共有兩列七條記錄,最左邊的是數據記錄的物理地址(注意邏輯上相鄰的記錄在磁盤上也并不是一定物理相鄰的)。為了加快Col2的查找,可以維護一個右邊所示的二叉查找樹,每個節點分別包含索引鍵值和一個指向對應數據記錄物理地址的指針,這樣就可以運用二叉查找在O(log2n)O(log2n)的復雜度內獲取到相應數據。
#3.hash索引 無法滿足范圍查找。
#4.二叉樹、紅黑樹 [復雜度O(h)]導致樹高度非常高(平衡二叉樹一個節點只能有左子樹和右子樹),邏輯上很近的節點(父子)物理上可能很遠,無法利用局部性,IO次數多查找慢,效率低。todo 邏輯上相鄰節點沒法直接通過順序指針關聯,可能需要迭代回到上層節點重復向下遍歷找到對應節點,效率低
B-Tree 和 B+Tree數據結構:
#4.B-Tree:
結構:B-TREE 每個節點都是一個二元數組: [key, data],所有節點都可以存儲數據。key為索引key,data為除key之外的數據。結構如下:
Mysql中B+Tree:在經典B+Tree的基礎上進行了優化,增加了順序訪問指針。在B+Tree的每個葉子節點增加一個指向相鄰葉子節點的指針,就形成了帶有順序訪問指針的B+Tree。這樣就提高了區間訪問性能:如果要查詢key為從18到49的所有數據記錄,當找到18后,只需順著節點和指針順序遍歷就可以一次性訪問到所有數據節點,極大提到了區間查詢效率(無需返回上層父節點重復遍歷查找減少IO操作)。
結構如下:
3.為什么Mysql選擇B+TREE索引? B+TREE索引有什么好處?
索引本身也很大,不可能全部存儲在內存中,因此索引往往以索引文件的形式存儲的磁盤上。這樣的話,索引查找過程中就要產生磁盤I/O消耗,相對于內存存取,I/O存取的消耗要高幾個數量級,所以索引的結構組織要盡量減少查找過程中磁盤I/O的存取次數,提升索引效率。
磁盤存取原理:
索引一般以文件形式存儲在磁盤上,索引檢索需要磁盤I/O操作。與主存不同,磁盤I/O存在機械運動耗費,因此磁盤I/O的時間消耗是巨大的。
圖5
一個磁盤由大小相同且同軸的圓形盤片組成,磁盤可以轉動(各個磁盤必須同步轉動)。在磁盤的一側有磁頭支架,磁頭支架固定了一組磁頭,每個磁頭負責存取一個磁盤的內容。磁頭不能轉動,但是可以沿磁盤半徑方向運動(實際是斜切向運動),每個磁頭同一時刻也必須是同軸的,即從正上方向下看,所有磁頭任何時候都是重疊的(不過目前已經有多磁頭獨立技術,可不受此限制)。
磁盤結構:
圖6
磁道: 每個同心環叫做一個 扇區: 磁盤的最小存儲單元。 當需要從磁盤讀取數據時,系統會將數據邏輯地址傳給磁盤,磁盤的控制電路按照尋址邏輯將邏輯地址翻譯成物理地址,即確定要讀的數據在哪個磁道,哪個扇區。為了讀取這個扇區的數據,需要將磁頭放到這個扇區上方,為了實現這一點,磁頭需要移動對準相應磁道,這個過程叫做尋道,所耗費時間叫做尋道時間,然后磁盤旋轉將目標扇區旋轉到磁頭下,這個過程耗費的時間叫做旋轉時間。
局部性原理與磁盤預讀:
由于存儲介質的特性,磁盤本身存取就比主存慢很多,再加上機械運動耗費,磁盤的存取速度往往是主存的幾百分分之一,因此為了提高效率,要盡量減少磁盤I/O。為了達到這個目的,磁盤往往不是嚴格按需讀取,而是每次都會預讀,即使只需要一個字節,磁盤也會從這個位置開始,順序向后讀取一定長度的數據放入內存。預讀可以提高I/O效率。預讀的長度一般為頁(page:計算機管理存儲器的邏輯塊-通常為4k)的整倍數. 主存和磁盤以頁為單位交換數據。當程序要讀取的數據不在主存中時,會觸發一個缺頁異常,此時系統會向磁盤發出讀盤信號,磁盤會找到數據的起始位置并向后連續讀取一頁或幾頁載入內存中。
B-/+Tree索引的性能優勢: 一般使用磁盤I/O次數評價索引優劣。
1.結合操作系統存儲結構優化處理: mysql巧妙運用操作系統存儲結構(一個節點分配到一個存儲頁中->盡量減少IO次數) & 磁盤預讀(緩存預讀->加速預讀馬上要用到的數據).
2.B+Tree 單個節點能放多個子節點,相同IO次數,檢索出更多信息。
3.B+TREE 只在葉子節點存儲數據 & 所有葉子結點包含一個鏈指針 & 其他內層非葉子節點只存儲索引數據。只利用索引快速定位數據索引范圍,先定位索引再通過索引高效快速定位數據。
詳解:Mysql設計利用了磁盤預讀原理,將一個B+Tree節點大小設為一個頁大小,在新建節點時直接申請一個頁的空間,這樣就能保證一個節點物理上存儲在一個頁里,加之計算機存儲分配都是按頁對齊的,這樣就實現了每個Node節點只需要一次I/O操作。
B-Tree索引、B+Tree索引: 單個節點能放多個子節點,查詢IO次數相同(mysql查詢IO次數最多3-5次-所以需要每個節點需要存儲很多數據)
B+TREE 只在葉子節點存儲數據 & 所有葉子結點包含一個鏈指針 & 其他內層非葉子節點只存儲索引數據。只利用索引快速定位數據索引范圍,先定位索引再通過索引高效快速定位數據。
B+Tree更適合外存索引,原因和內節點出度d有關。從上面分析可以看到,d越大索引的性能越好,而出度的上限取決于節點內key和data的大小:
B+Tree內節點去掉了data域,因此可以擁有更大的出度,擁有更好的性能。只利用索引快速定位數據索引范圍,先定位索引再通過索引高效快速定位數據。
dmax=floor(pagesize/(keysize+datasize+pointsize))
Mysql 索引實現-MyISAM & InnoDB: important
聚簇索引: 索引 和 數據文件為同一個文件。非聚簇索引: 索引 和 數據文件分開的索引。
MyISAM & InnoDB 都使用B+Tree索引結構。但是底層索引存儲不同,MyISAM 采用非聚簇索引,而InnoDB采用聚簇索引。
MyISAM索引原理:采用非聚簇索引-MyISAM myi索引文件和myd數據文件分離,索引文件僅保存數據記錄的指針地址。葉子節點data域存儲指向數據記錄的指針地址。(底層存儲結構: frm -表定義、 myi -myisam索引、 myd-myisam數據)
MyISAM索引按照B+Tree搜索,如果指定的Key存在,則取出其data域的值,然后以data域值-數據指針地址去讀取相應數據記錄。輔助索引和主索引在結構上沒有任何區別,只是主索引要求key是唯一的,而輔助索引的key可以重復。MyISAM索引樹如下:
MyISAM非聚簇索引
InnoDB優勢:高擴展性,充分發揮硬件性能、 Crash Safe、 支持事務、 可以在線熱備份
InnoDB特性:
1. 事務支持(ACID)2. 擴展性優良 3. 讀寫不沖突 4. 緩存加速
2. 功能組件: redo/undo & 異步IO & MVCC & 行級別鎖 & Page Cache(LRU)
InnoDB物理存儲結構如下圖:
InnoDB表空間管理
InnoDB物理存儲文件結構說明:
InnoDB以表空間Tablespace(idb文件)結構進行組織,每個Tablespace 包含多個Segment段,每個段(分為2種段:葉子節點Segment&非葉子節點Segment), 一個Segment段包含多個Extent,一個Extent占用1M空間包含64個Page(每個Page 16k),InnoDB B-Tree 一個邏輯節點就分配一個物理Page,一個節點一次IO操作。,一個Page里包含很多有序數據Row行數據,Row行數據中包含Filed屬性數據等信息。
? 表空間(ibd文件)
? 段(一個索引2段:葉子節點Segment & 非葉子節點Segment)
? Extent(1MB):一個Extent(1M) 包含64個 Page(16k),一個Page里包含很多有序行數據 , InnoDB B-Tree 一個邏輯節點就分配一個物理Page,一個節點一次IO操作。
? Page(16KB)
? Row
? Field
表插入數據擴展原理: 一次擴張一個Extent空間(1M),64個Page,按照順序結構向每個page中插入順序。
InnoDB邏輯組織結構:
InnoDB索引樹結構
每個索引一個B+樹, 一個B+樹節點 = 一個物理Page(16K)
? 數據按16KB切片為Page 并編號, 編號可映射到物理文件偏移(16K * N), B+樹葉子節點前后形成雙向鏈表, 數據按主鍵索引聚簇, 二級索引葉節點存儲主鍵值, 通過葉節點主鍵值回表查找數據。
InnoDB索引原理:
采用聚簇索引- InnoDB數據&索引文件為一個idb文件,表數據文件本身就是主索引,相鄰的索引臨近存儲。 葉節點data域保存了完整的數據記錄(數據[除主鍵id外其他列data]+主索引[索引key:表主鍵id])。 葉子節點直接存儲數據記錄,以主鍵id為key,葉子節點中直接存儲數據記錄。(底層存儲結構: frm -表定義、 ibd: innoDB數據&索引文件)
注:由于InnoDB采用聚簇索引結構存儲,索引InnoDB的數據文件需要按照主鍵聚集,因此InnoDB要求表必須有主鍵(MyISAM可以沒有)。如果沒有指定mysql會自動選擇一個可以唯一表示數據記錄的列作為主鍵,如果不存在這樣的列,mysql自動為InnoDB表生成一個隱含字段(6個字節長整型)作為主鍵。 InnoDB的所有 輔助索引 都引用 數據記錄的主鍵 作為data域。
聚簇索引這種實現方式使得按主鍵的搜索十分高效,但是輔助索引搜索需要檢索兩遍索引:首先檢索輔助索引獲得數據記錄主鍵,然后用主鍵到主索引中檢索獲得數據記錄。InnoDB聚簇索引結構:
InnoDB聚簇索引
索引查找流程:
#1.索引精確查找: 確定定位條件, 找到根節點Page No, 根節點讀到內存, 逐層向下查找, 讀取葉子節點Page,通過 二分查找找到記錄或未命中。(select * from user_info where id = 23)
索引精確查找過程
#2.索引范圍查找:讀取根節點至內存, 確定索引定位條件id=18, 找到滿足條件第一個葉節點
, 順序掃描所有結果, 直到終止條件滿足id >=22 (select * from user_info where id >= 18 and id < 22)
索引范圍查找過程
#3.全表掃描:直接讀取葉節點頭結點, 順序掃描, 返回符合條件記錄, 到最終節點結束
(select * from user_info where name = 'abc')
全表掃描過程
#4.二級索引查找
二級索引查找過程
Create table table_x(int id primary key, varchar(64) name,key sec_index(name) )
? Select * from table_x where name = “d”;
通過二級索引查出對應主鍵,拿主鍵回表查主鍵索引得到數據, 二級索引可篩選掉大量無效記錄,提高效率
Innodb對索引的優化 Insert Buffer todo
Innodb Buffer Pool: todo
Innodb 異步IO框架:
Innodb ACID如何保證:
?原子性 redo + undo ? 一致性 redo ? 隔離性 鎖 + MVCC ? 持久性 redo
Innodb Redo日志:
Innodb 行級別鎖:
二、MySQL高級 之 explain執行計劃詳解
使用explain關鍵字可以模擬優化器執行SQL查詢語句,從而知道MySQL是如何處理你的SQL語句的,分析你的查詢語句或是表結構的性能瓶頸。
explain執行計劃包含的信息
其中最重要的字段為:id、type、key、rows、Extra
各字段詳解
一、id
select查詢的序列號,包含一組數字,表示查詢中執行select子句或操作表的順序
三種情況:
1、id相同:執行順序由上至下
2、id不同:如果是子查詢,id的序號會遞增,id值越大優先級越高,越先被執行
3、id相同又不同(兩種情況同時存在):id如果相同,可以認為是一組,從上往下順序執行;在所有組中,id值越大,優先級越高,越先執行
二、select_type
查詢的類型,主要是用于區分普通查詢、聯合查詢、子查詢等復雜的查詢
1、SIMPLE:簡單的select查詢,查詢中不包含子查詢或者union
2、PRIMARY:查詢中包含任何復雜的子部分,最外層查詢則被標記為primary
3、SUBQUERY:在select 或 where列表中包含了子查詢
4、DERIVED:在from列表中包含的子查詢被標記為derived(衍生),mysql或遞歸執行這些子查詢,把結果放在零時表里
5、UNION:若第二個select出現在union之后,則被標記為union;若union包含在from子句的子查詢中,外層select將被標記為derived
6、UNION RESULT:從union表獲取結果的select
三、type
訪問類型,sql查詢優化中一個很重要的指標,結果值從好到壞依次是:
system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL
一般來說,好的sql查詢至少達到range級別,最好能達到ref
1、system:表只有一行記錄(等于系統表),這是const類型的特例,平時不會出現,可以忽略不計
2、const:表示通過索引一次就找到了,const用于比較primary key 或者 unique索引。因為只需匹配一行數據,所有很快。如果將主鍵置于where列表中,mysql就能將該查詢轉換為一個const
3、eq_ref:唯一性索引掃描,對于每個索引鍵,表中只有一條記錄與之匹配。常見于主鍵 或 唯一索引掃描。
注意:ALL全表掃描的表記錄最少的表如t1表
4、ref:非唯一性索引掃描,返回匹配某個單獨值的所有行。本質是也是一種索引訪問,它返回所有匹配某個單獨值的行,然而他可能會找到多個符合條件的行,所以它應該屬于查找和掃描的混合體
5、range:只檢索給定范圍的行,使用一個索引來選擇行。key列顯示使用了那個索引。一般就是在where語句中出現了bettween、、in等的查詢。這種索引列上的范圍掃描比全索引掃描要好。只需要開始于某個點,結束于另一個點,不用掃描全部索引
6、index:Full Index Scan,index與ALL區別為index類型只遍歷索引樹。這通常為ALL塊,應為索引文件通常比數據文件小。(Index與ALL雖然都是讀全表,但index是從索引中讀取,而ALL是從硬盤讀取)
7、ALL:Full Table Scan,遍歷全表以找到匹配的行
四、possible_keys
查詢涉及到的字段上存在索引,則該索引將被列出,但不一定被查詢實際使用
五、key
實際使用的索引,如果為NULL,則沒有使用索引。
查詢中如果使用了覆蓋索引,則該索引僅出現在key列表中
六、key_len
表示索引中使用的字節數,查詢中使用的索引的長度(最大可能長度),并非實際使用長度,理論上長度越短越好。key_len是根據表定義計算而得的,不是通過表內檢索出的
七、ref
顯示索引的那一列被使用了,如果可能,是一個常量const。
八、rows
根據表統計信息及索引選用情況,大致估算出找到所需的記錄所需要讀取的行數
九、Extra
不適合在其他字段中顯示,但是十分重要的額外信息
1、Using filesort :
mysql對數據使用一個外部的索引排序,而不是按照表內的索引進行排序讀取。也就是說mysql無法利用索引完成的排序操作成為“文件排序”
由于索引是先按email排序、再按address排序,所以查詢時如果直接按address排序,索引就不能滿足要求了,mysql內部必須再實現一次“文件排序”
2、Using temporary:
使用臨時表保存中間結果,也就是說mysql在對查詢結果排序時使用了臨時表,常見于order by 和 group by
3、Using index:
表示相應的select操作中使用了覆蓋索引(Covering Index),避免了訪問表的數據行,效率高
如果同時出現Using where,表明索引被用來執行索引鍵值的查找(參考上圖)
如果沒用同時出現Using where,表明索引用來讀取數據而非執行查找動作
覆蓋索引(Covering Index):也叫索引覆蓋。就是select列表中的字段,只用從索引中就能獲取,不必根據索引再次讀取數據文件,換句話說查詢列要被所建的索引覆蓋。
注意:
a、如需使用覆蓋索引,select列表中的字段只取出需要的列,不要使用select *
b、如果將所有字段都建索引會導致索引文件過大,反而降低crud性能
4、Using where :
使用了where過濾
5、Using join buffer :
使用了鏈接緩存
6、Impossible WHERE:
where子句的值總是false,不能用來獲取任何元祖
7、select tables optimized away:
在沒有group by子句的情況下,基于索引優化MIN/MAX操作或者對于MyISAM存儲引擎優化COUNT(*)操作,不必等到執行階段在進行計算,查詢執行計劃生成的階段即可完成優化
8、distinct:
優化distinct操作,在找到第一個匹配的元祖后即停止找同樣值得動作
綜合Case
執行順序
1(id = 4)、【select id, name from t2】:select_type 為union,說明id=4的select是union里面的第二個select。
2(id = 3)、【select id, name from t1 where address = ‘11’】:因為是在from語句中包含的子查詢所以被標記為DERIVED(衍生),where address = ‘11’ 通過復合索引idx_name_email_address就能檢索到,所以type為index。
3(id = 2)、【select id from t3】:因為是在select中包含的子查詢所以被標記為SUBQUERY。
4(id = 1)、【select d1.name, … d2 from … d1】:select_type為PRIMARY表示該查詢為最外層查詢,table列被標記為 “derived3”表示查詢結果來自于一個衍生表(id = 3 的select結果)。
5(id = NULL)、【 … union … 】:代表從union的臨時表中讀取行的階段,table列的 “union 1, 4”表示用id=1 和 id=4 的select結果進行union操作。
三、總結
一、為什么選擇B+Tree。
每次磁盤讀取都是一頁數據,4k的倍數,我們就算16KB,索引存儲的KEY為int為4B或者bigInt8B,指針一般也為4B或者8B,這樣每頁可以存儲的數據為 16KB/(8B+8B)=1K,也就是磁盤每次可以讀取1K的索引數據個數,也就表示n層數據可以存儲K的n次冪條數據,假如1K=1000,1億條數據就代表只需要3層樹,磁盤查詢效果就只需要3次就可以了;如果使用B-Tree,因為它非葉子節點也會存儲數據包,所以他每頁存儲的數據為16KB/(8B+8B+數據大小)<<1K,數量一定的情況,每頁存儲數據降低,那就表示樹的高度增加,查詢是每頁預讀,層層往下查找,樹的高度代表查詢磁盤的次數,磁盤查詢效率低下,因此也代表,查詢次數過多。
二、為什么查詢需要建立索引。
如果不建立索引,則表示全表掃描,葉子節點的存儲了key,指針和數據,每頁存儲的數據量非常少,全表掃描需要從前往后遍歷葉子節點,查詢的數據大多數不可能在第一頁就命中,數據頁數越靠后,讀取磁盤的次數越多,查詢效率就越慢,所以需要建立索引,每一個索引都是一棵樹,使用主鍵索引效率最高,輔助索引是通過輔助索引查詢到主鍵索引,之后查詢數據,就按3層數據運算,也只需要6次磁盤讀取就可以定位數據,也會遠遠大于全表遍歷;聯合索引是多個數據拼接的一個數據作為索引,所以在order by,group by多個字段的時候,索引順序需要匹配,order by 默認使用asc,可以建立當存在desc和asc時可以使用虛擬列建立索引,例子:
ALTER TABLE `veh_signal_fault`
ADD COLUMN `fault_order_by` varchar(255) GENERATED ALWAYS AS (CONCAT(is_receive,LPAD(timestampdiff(second ,end_time,'9999-01-01 12:00:00'),16,'0'),fault_level)) VIRTUAL。
三、為什么索引不宜過多,聯合索引需要慎用。
因為每一個索引都是一個B+Tree,每次刪除和新增都可能破壞所有的索引樹,修改可能會破壞索引樹,破壞了索引樹,系統就需要把索引樹重新平衡,這樣就需要消耗性能,當索引過多,那每次新增、刪除和修改就需要消耗更多的性能,所以索引不宜過多,聯合索引需要先拼接,而且樹的高度超過了普通索引,性能消耗更大,所以聯合索引需要慎用。
第四、選擇建多個列的復合索引還是建多個單個列的索引。
索引提高性能的地方在于減少磁盤的讀取次數,首先,B+TREE一般只會使用一個索引,當查詢條件有多個的情況,可能命中率不會太高,導致查詢到的數據塊還是過多,需要查詢磁盤的次數過高,即使能使用多個索引,查詢磁盤的次數也會多余復合索引,所以當業務能滿足復合索引的情況,就盡量建復合索引,具體情況具體分析。
第五、MYSQL的索引建立規則。
1、MYSQL一般使用一個索引。
2、 在WHERE條件后的查詢信息盡量使用=(聯合索引)。
3、范圍查找會使得后面的匹配條件失效(聯合索引),所以根據實際情況,最好把范圍查找放在后面,where條件有多少個信息,它就會打散的遍歷多少遍,所以需要根據實際情況調整位置。
4、當索引不生效時,可以使用強制索引(前提條件是強制索引真的能提高效率):SELECT * FROM TABLE FORCE INDEX(強制索引名稱:INDEX_FLAG_PRO_BEGIN_HANDLE_COMAPNNY_BUS) WHERE 1=1;
參考:
http://blog.codinglabs.org/articles/theory-of-mysql-index.html
https://segmentfault.com/a/1190000004690721
Mysql索引和查詢優化
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的mysql explain详解_数据库mysql(1)——B+TREE索引原理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 联想电脑使用优盘启动不了怎么办 联想电脑
- 下一篇: 图像语义分割python_遥感图像语义分