《永劫無間》長槍連招教學
目的
使以下查詢速度更快:
- 按照id查詢唯一一條記錄
- 按照某些個欄位查詢對應的記錄
- 查詢某個範圍的所有記錄(between and)
- 對查詢出來的結果排序
本質
通過不斷的縮小想要獲取資料的範圍來篩選出最終想要的結果,同時把隨機的事件變成順序的事件,也就是說,通過索引機制,我們可以總是用同一種查詢方式來鎖定資料。
頁與磁碟
磁碟塊:檔案系統與磁碟互動的最小單位,一個磁碟塊由連續幾個扇區組成,一般大小為4KB
頁:MySQL中和磁碟互動的最小單位稱為頁,是一種資料結構,預設為16KB,相當於4個磁碟塊。
B-樹和B+樹
區別
- B+樹除葉子節點之外其他節點值儲存關鍵字和指向子節點的指標,B-樹還儲存了資料,同樣大小情況下,B+樹可以儲存更多的關鍵字
- B+樹葉子節點中儲存了所有關鍵字和data,並且多個節點用連結串列連線,子節點從左到右是有序的,可以支援範圍查詢。
如何選擇
- B-樹非葉子節點也儲存資料,在查詢某個關鍵字的時候找到即可返回。在同樣高度的兩種樹中,B-樹查詢某個關鍵字的效率更高。
- 在找大於某個關鍵字或者小於某個關鍵字的資料的時候,B+樹只需要找到該關鍵字然後沿著連結串列遍歷即可,但B-樹需要遍歷該關鍵字結點的根結點去搜索。
- 同樣總量的資料,B-樹的深度會更大,增大查詢時的磁碟I/O次數,進而影響查詢效率。
MySQL的儲存引擎和索引
InnoDB
兩種索引
主鍵索引/聚集:每個表只有一個主鍵索引,葉子節點同時儲存主鍵的值和資料
輔助索引/非聚集:葉子節點儲存了索引欄位的值以及主鍵的值
輔助索引查詢的過程叫回表:先在輔助索引中檢索到資料,獲取id,然後通過id在主鍵索引中檢索。
MyISAM
兩種索引的葉子節點儲存了索引欄位的值以及資料記錄的地址。
過程:在索引中找到關鍵字,獲取地址,通過地址找到資料。
為什麼InnoDB的輔助索引不存地址?
表中的資料發生變更的時候,會影響其他記錄地址的變化,如果輔助索引中記錄資料的地址,此時會受影響,而主鍵的值一般是很少更新的,當頁中的記錄發生地址變更的時候,對輔助索引是沒有影響的。
索引分類
-
主鍵索引
每個表都會有,整個表的資料儲存在聚集索引中,B+樹,非葉子節點存主鍵的值,葉子節點存主鍵的值+記錄的資料
表中未指定主鍵時,自動給每條記錄新增隱藏的rowid欄位作為主鍵
聚集索引
-
輔助索引
非聚集索引
b+樹,非葉子節點存索引欄位,葉子節點存索引欄位和主鍵的值
- 單列索引:一個索引只包含一個列
- 多列索引(複合索引):一個索引包含多個列
- 唯一索引:索引列的值必須唯一,允許有一個空值
CREATE [UNIQUE] INDEX 索引名稱 ON 表名(列名);
SHOW INDEX FROM 表名; 檢視索引資訊
EXPLAIN select * from test1 where sex=2 and name='javacode3500000';
最左匹配原則
當b+樹的資料項是複合的資料結構,比如(name,age,sex)的時候,b+樹是按照從左到右的順序來建立搜尋樹的,比如當(張三,20,F)這樣的資料來檢索的時候,b+樹會優先比較name來確定下一步的所搜方向,如果name相同再依次比較age和sex,最後得到檢索的資料;但當(20,F)這樣的沒有name的資料來的時候,b+樹就不知道下一步該查哪個節點,因為建立搜尋樹的時候name就是第一個比較因子,必須要先根據name來搜尋才能知道下一步去哪裡查詢。比如當(張三,F)這樣的資料來檢索時,b+樹可以用name來指定搜尋方向,但下一個欄位age的缺失,所以只能把名字等於張三的資料都找到,然後再匹配性別是F的資料了, 這個是非常重要的性質,即索引的最左匹配特性。
eg:
按照[a,c]兩個欄位查詢
這種只能利用到索引中的a欄位了,通過a確定索引範圍,然後載入a關聯的所有記錄,再對c的值進行過濾。
查詢a=1 and b>=0 and c=1的記錄
這種情況只能先確定a=1 and b>=0所在頁的範圍,然後對這個範圍的所有頁進行遍歷,c欄位在這個查詢的過程中,是無法確定c的資料在哪些頁的,此時我們稱c是不走索引的,只有a、b能夠有效的確定索引頁的範圍。
類似這種的還有>、<、between and,多欄位索引的情況下,mysql會一直向右匹配直到遇到範圍查詢(>、<、between、like)就停止匹配。
索引區分度
索引區分度 = count(distinct 記錄) / count(記錄)
當索引區分度高的時候,檢索資料更快一些,索引區分度太低,說明重複的資料比較多,檢索的時候需要訪問更多的記錄才能夠找到所有目標資料。
當索引區分度非常小的時候,基本上接近於全索引資料的掃描了,此時查詢速度是比較慢的。
建立索引時,儘量選擇區分度高的列。
當多個條件中有索引的時候,並且關係是and的時候,會走索引區分度高的
覆蓋索引
查詢的內容只包含索引欄位+id,這個時候在進行輔助索引的檢索的時候,就已經可以得到select的全部資料,就不需要進行回表繼續檢索主鍵索引。
所以寫sql的時候,儘量避免使用*
,*
可能會多一次回表操作,需要看一下是否可以使用索引覆蓋來實現,效率更高一些。
索引下推
ICP(Index Condition Pushdown),MySQL5.6的新特性,使用索引過濾資料的一種優化方式,減少儲存引擎訪問及表的次數以及MySQL伺服器訪問儲存引擎的次數。
eg:
我們需要查詢name以javacode35
開頭的,性別為1的記錄數,sql如下
select **count**(id) from test1 a where name like 'javacode35%' and sex = 1;
name為輔助索引的過程
- 走name索引檢索出以javacode35的第一條記錄,得到記錄的id
- 利用id去主鍵索引中查詢出這條記錄R1
- 判斷R1中的sex是否為1,然後重複上面的操作,直到找到所有記錄為止。
上面的過程中需要走name索引以及需要回表操作。
採用ICP的方式,建立一個(name,sex)的組合索引,查詢過程如下:
- 走(name,sex)索引檢索出以javacode35的第一條記錄,可以得到(name,sex,id),記做R1
- 判斷R1.sex是否為1,然後重複上面的操作,知道找到所有記錄為止
這個過程中不需要回表操作了,通過索引的資料就可以完成整個條件的過濾,速度比上面的更快一些。
使用索引的小tips
- 在區分度高的欄位上面建立索引可以有效的使用索引,區分度太低,無法有效的利用索引,可能需要掃描所有資料頁,此時和不使用索引差不多
- 聯合索引注意最左匹配原則:必須按照從左到右的順序匹配,mysql會一直向右匹配直到遇到範圍查詢(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)順序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引則都可以用到,a,b,d的順序可以任意調整
- 查詢記錄的時候,少使用*,儘量去利用索引覆蓋,可以減少回表操作,提升效率
- 有些查詢可以採用聯合索引,進而使用到索引下推(ICP),也可以減少回表操作,提升效率
- 禁止對索引欄位使用函式、運算子操作,會使索引失效
- 字串欄位和數字比較的時候會使索引無效
- 模糊查詢'%值%'會使索引無效,變為全表掃描,但是'值%'這種可以有效利用索引
- 排序中儘量使用到索引欄位,這樣可以減少排序,提升查詢效率