mysql面試看這篇就夠了

一、什麼是索引

  • 索引可以類比成書本的目錄方便我們查找數據使用

二、爲什麼需要索引

  • 想想我們平時在書本上查找資料時,如果沒有目錄的話我們得一頁一頁的去看才能找到自己想要的這個查找過程是極其慢的,但是我們有了目錄就不一樣了,我們只需先查閱目錄然後快速的定位到我們需要的頁
  • 系統設計大師根據生活經驗,於是呼將我們的計算機文件系統也設計成類似書本一樣擁有個一個類似目錄的存儲結構,稱之爲索引

三、Mysql常見的幾種索引數據結構

Mysql表常用索引結構有兩種BTree和Hash其中BTree又分爲B-Tree和B+Tree本文只介紹BTree

  • 在談Mysql常用的幾種索引結構之前我們先回憶一下我們學過的二叉查找樹,下圖以二叉查找樹爲索引結構給student_info表建了個索引其中id爲主鍵索引字段
    二叉查找樹索引
    我們要查找王五的信息只需先在索引中找到id=7的節點(比較了三次)然後根據節點指向的內存讀取王五的信息即可,速度很快比在表中一行一行查快多了,這樣看來用二叉查找樹當Mysql的表索引結構似乎很完美,其實不然,在實際生產中,圖中的student _info表存儲的數據可能會有幾百萬條,這個時候二叉查找樹的高都會陡然上升,隨之查找效率也陡然下降,在這裏還要普及一個知識點記住即可,Mysql每個索引節點佔用一個磁盤頁(大小一般爲4K)哪怕節點數據再小也會分配一個磁盤頁給此節點,每讀取一個節點數據(即一個磁盤頁)需要做一次I/O操作,I/O操作對於CPU的運行速度來講效率是相當低下的,隨着數據的增多,樹的高度隨之曾高,I/O操作也跟着變多,這個時候我們就不得不考慮如何讓每個節點存儲更多的數據,以降低樹的高度,減少I/O操作次數,由此引申出了B-Tree這種數據結構
  • 在談BTree之前我們先普及一下磁盤讀取數據的過程,下圖是一個簡易的磁盤尋址示意圖,I/O設備在接收到CPU查詢數據的指令時,首先磁頭會在不同的磁道移動這個過程比較慢,當確定了數據存在哪個磁道就會讀取數據,這個過程比較快,由此可知從磁盤讀取數據時間主要消耗在尋道上
    磁盤尋址示意圖
  • B-Tree的定義首先B-Tree是一顆多路平衡查找樹,它的每個節點最多擁有k孩子,k被稱爲B-Tree的階,k的大小由磁盤頁的大小決定(磁盤頁的大小÷節點中存儲的記錄大小=k -1)

1.根結點至少有兩個孩子節點。
2.每個中間節點都包含k-1個元素和k個孩子,其中 m/2 <= k <= m
3.每一個葉子節點都包含k-1個元素,其中 m/2 <= k <= m
4.所有的葉子結點都位於同一層。
5.每個節點中的元素從小到大排列,節點當中k-1個元素正好是k個孩子包含的元素的值域分劃。

  • 咱們以B-Tree爲索引結構做出student_info表在內存中的結構圖(其中data表示,表中的一條數據記錄或指向表記錄的文件指針,這裏爲了作圖方便因此直接用data表示),此B-Tree爲三階B樹,由圖可知,由於B-Tree樹每個節點可以存儲多條記錄,較上面的二叉查找樹的“高瘦”特點來說B-Tree變成了“矮胖”,比如我們要查找王五的信息首先id=4的節點加載進內存(一次I/O操作)與王五的id進行比較,接着將id=4的節點的右孩子加載進內存(一次I/O操作)進行比較,接着講id=7的節點加載進內存(一次I/O操作),分析可知,當數據量很大時,我們可以適當提高B-Tree的度,讓B-Tree變的“矮胖”,這樣就減少了比較次數,I/O操作的次數
    B-Tree索引結構圖
  • 雖然B-Tree相對於二叉查找樹來說大大提高了效率(減少了I/O次數)但是還不夠,由於實際系統中確定B-Tree的度k的大小,其中一個因素之一就是取決於磁盤頁大小÷記錄大小=k -1在磁盤頁大小(4k)已知的情況下只有減小data(記錄)的大小才能提高B-Tree的度k,由此引申出B+Tree的概念,B-Tree與B+Tree最大的差別之一就是,B-Tree將數據(這裏的數據同上文,中真正的記錄,或者文件指針)與索引存在各個點中,而B+Tree非葉節點只存儲索引字段,而葉子節點才存儲真正的記錄,這樣就大大提高了B+Tree的度從而從而降低了B+Treed的高度在查詢數據時減少了I/O操作次數
  • B+Tree的定義 首先B+樹是B樹的一種變形形式,B+樹上的葉子結點存儲索引字段以及相應記錄的地址(又或者是記錄),葉子結點以上各層作爲索引使用。一棵m階的B+樹定義如下

1、每個結點至多有m個子女
2、除根結點外,每個結點至少有[m/2]個子女,根結點至少有兩個子女
3、有k個子女的結點必有k個關鍵字B+樹的查找與B樹不同,當索引部分某個結點> 的關鍵字與所查的關鍵字相等時,並不停止查找,應繼續沿着這個關鍵字左邊的指針向下,一直查到該關鍵字所在的葉子結點爲止
看完定義之後我們再來B+Tree的結構長什麼樣,如下圖所示,其中的data表示數據或者文件指針
B+Tree示意圖

四、Mysql結合B+Tree數據結構進行存儲的細節

經過以上分析我們已經知道了B+Tree是一顆多路平衡查找樹,數據都存儲在葉子>節點,非葉子節點只存儲索引字段的值,並且葉子節點之間是通過指針進行連接的

  • 在談細節之前們必須先明確一下幾個概念
    • 索引文件與數據文件之間的關係分爲以下兩種關係,有興趣的同學可以新建兩個表分別採用InnoDBMyISAM存儲引擎進行存儲,然後打開mysql安裝目錄下的的數據庫文件就可以看到如下幾個文件(我這裏事先建好了兩個表分別採用用不同的存儲引擎),其中表table_1是採用InnDB存儲引擎進行存儲的,由於InnoDB是採用聚集索引進行存儲的,我們可以看到table_1表只有一個table_1.ibd文件用來存儲索引和數據,table_2是採用MyISAM進行存儲的,由於MyISAM存儲引擎是採用非聚集索引的,我們可以看到表table_2有兩個文件table_2.MYD用來存儲數據,table_2.MYI用來存儲索引,經以上分析我們總結如下兩點
      在這裏插入圖片描述
      • ①聚集索引: 可以形象的理解爲,數據都存儲在葉子節點,即索引與數據文件存儲在一起InnoDB存儲引擎就是採用的聚集索引進行存儲的
      • ②非聚集索引: 可以形象的理解爲,葉子節點存儲數據的文件指針,即索引與數據文件是分卡存儲的MyISAM存儲引擎就是採用非聚集索引進行存儲的
    • 什麼是主鍵索引,什麼又是輔助索引,什麼又是聯合索引?
      • 主鍵索引: 可以理解爲以主鍵爲B+Tree的樹節點,葉子節點存儲數據或者文件指針
      • 輔助索引: 可以理解爲非主鍵字段加了索引,非主鍵字段由於加了索引也會在系統中形成一顆B+樹的索引結構,與主鍵索引形成的B+樹結構唯一不同的就是,葉子節點指向的是主鍵索引(大家可能會有疑問,當我們沒有指定主鍵索引時,該怎麼辦,如果沒有指定時,系統會選擇一個重複率最低的字段爲主鍵索引,如果這樣的字段也不存在的話,系統會採用,後臺生成的,ROW_ID,爲索引值,進行存儲),爲什麼這麼做? 這樣避免了數據的重複存儲
      • 聯合索引: 可以理解爲同時指定多個字段爲索引字段,怎麼理解呢?比如我們有張學生表,有id,name我們可以指定index(id,name)爲索引字段,可以簡單的認爲id,name變成了一個整體,作爲索引字段

五、使用索引時需要注意些什麼

  • 主鍵索引儘量採用自增的數字 ,爲什麼呢? 一句話可以減少磁盤碎片,降低B+Tree的變換次數,詳解如下
    • 在這裏我們還需要明確一定的是,雖然採用B+Tree樹進行索引結構的建立,提高了查詢速度,但是新增或刪除數據時,B+Tree需要進行變換(怎麼變換是一個比較複雜的工程,後期會轉門出文章進行講解),以保持身的特點(具體是什麼特點可以參考前文說的B+Tree的定義),這個變換的過程代價是不會有忽略的
    • 結合上面分析,我們知道Mysql採用B+Tree結構進行索引的建立,每個非葉子節點大小等於4k,剛好是一個磁盤頁的大小
    • 如果我們採用自增的數字爲主鍵索引,再結合B+Tree的特點(節點內的元素都是從小到大排列的),這樣我們在每新增一個節點時,都會按順序的一個一個節點排滿,而後才進行分裂,這樣不但減少了B+Tree變換的次數也減少了磁盤碎片,刪除節點也同理,如果不太理解的話可以利用此網站進行一個B+Tree新增OR刪除數據的節點的模擬操作
  • 使用聯合索引時我們需要注意什麼? 一句話最左匹配,詳解如下
    • 首先假設我們有張student表,表中有A,B,C三個字段我們現在建立聯合索引index(A,B,C)
    • 使用index(A),index(A,B),index(A,B,C)能命中索引
    • 使用index(A,C),index(B,C)不能命中索引

六、explain SQL分析工具介紹

explain打印參數圖

  • id: 選擇標識符(id越大越先執行)
  • select_type: 表示查詢的類型(分爲(SIMPLE)和複雜查詢(PRIMARY)兩大類)複雜查詢又分爲:簡單子查詢、派生表(from 語句中的子查詢)、union 查詢
  • table: 查詢的是那張表(注意:mysql 能夠在優化階段分解查詢語句,在執行階段用不着再訪問表或索引,例如:在索引列中選取最小值,可以單獨查找索引來完成,不需要在執行時訪問表)
  • partitions: 匹配的分區(當數據庫有進行分區時,顯示操作的是那個分區)
  • type: 表示關聯類型或訪問類型,即 MySQL 決定如何查找表中的行,查找數據行記錄的大概範圍(依次從最優到最差分別爲:system > const > eq_ref > ref > range > index > ALL一般來說,得保證查詢達到range級別,最好達到ref
    • system&constant: mysql 能對查詢的某部分進行優化並將其轉化成一個常量用於 primary key 或 unique key 的所有列與常數比較時,所以表最多隻有一條行記錄匹配,讀取 1 次,速度比較快,system 是 const 的特例,表裏只有一條元組匹配時爲system
    • eq_ref: primary key 或 unique key 索引的所有部分被連接使用 ,最多隻會返回一條符合條件的記錄,這可能是在 const 之外最好的聯接類型了,簡單的select 查詢不會出現這種 type
    • ref: 相比 eq_ref,不使用唯一索引,而是使用普通索引或者唯一性索引的部分前綴,索引要和某個值相比較,可能會找到多個符合條件的行
    • range: 範圍掃描通常出現在 in(), between ,> ,<, >= 等操作中。使用一個索引來檢索給定範圍的行
    • index: 掃描全表索引,這通常比 ALL 快一些(index 是從索引中讀取的,而 all 是從硬盤中讀取)
    • ALL: 即全表掃描,意味着 mysql 需要從頭到尾去查找所需要的行,通常情況下這需要增加索引來進行優化了
  • possible_keys: 表示查詢時,可能使用的索引(有時可能出現possible_keys 有值,而 key 顯示 NULL 的情況,這種情況是因爲表中數據不多,mysql 認爲索引對此查詢幫助不大,選擇了全表查詢,如果該列是 NULL,則沒有相關的索引,在這種情況下,可以通過檢查 where 子句看看是否可以添加一個適當的索引來提高查詢性能,然後用 explain 查看效果)
  • key: 表示實際使用的索引
  • key_len: 索引字段的長度
  • ref: 列與索引的比較
  • rows: 掃描出的行數(估算的行數)
  • filtered: 按表條件過濾的行百分比
  • Extra: 執行情況的描述和說明
    • Using index: 查詢的列被索引覆蓋,並且 where 篩選條件是索引的前導列,是性能高的表現,一般是使用了覆蓋索引(索引包含了所有查詢的字段)對於 InnoDBb 來說,如果是輔助索引性能會有不少提高
    • Using where: 查詢的列未被索引覆蓋,where 篩選條件非索引的前導列
    • Using where Using index: 查詢的列被索引覆蓋,並且 where 篩選條件是索引列之一但是不是索引的前導列,意味着無法直接通過索引查找來查詢到符合條件的數據
    • NULL: 查詢的列未被索引覆蓋,並且 where 篩選條件是索引的前導列,意味着用到了索引,但是部分字段未被索引覆蓋,必須通過“回表”來實現,不是純粹地用到了索引,也不是完全沒用到索引
    • Using index condition: 與 Using where 類似,查詢的列不完全被索引覆蓋,where 條件中是一個前導列的範圍
    • Using temporary: Mysql 需要創建一張臨時表來處理查詢,出現這種情況一般是要進行優化的,首先是想到用索引來優化
    • Using filesort: Mysql 會對結果使用一個外部索引排序,而不是按索引次序從表裏讀取行,此時 mysql 會根據聯接類型瀏覽所有符合條件的記錄,並保存排序關鍵字和行指針,然後排序關鍵字並按順序檢索行信息。這種情況下一般也是要考慮使用索引來優化的

七、優化SQL原則

  • 最佳左前綴法則: 如果索引了多列,要遵守最左前綴法則。指的是查詢從索引的最左前列開始並且不跳過索引中的列
  • 不在索引列上做任何操作(計算、函數、(自動 or 手動)類型轉換),會導致索引失效而轉向全表掃描
  • 存儲引擎不能使用索引中範圍條件右邊的列
  • 儘量使用覆蓋索引(只訪問索引的查詢(索引列包含查詢列)),減少 select *語句
  • mysql 在使用不等於(!=或者<>)的時候無法使用索引會導致全表掃描
  • is null,is not null 也無法使用索引
  • like 以通配符開頭(’$abc…’)mysql 索引失效會變成全表掃描操作
  • 字符串不加單引號索引失效
  • 少用 or,用它連接時很多情況下索引會失效

八、mysql鎖分析

  • 後續更新
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章