mysql 作為一個關系型資料庫,在國內使用應該是最廣泛的,也許你司使用 Oracle、Pg 等等,但是大多數互聯網公司,比如我司使用得最多的還是 Mysql,重要性不言而喻,
事情是這樣的,上一篇關于 MySQL 基礎架構的文章發出以后,有小伙伴說能不能聊聊索引?日常作業中,我們遇到 sql 執行慢的時候,經常會收到這樣的建議:"加個索引唄",索引究竟是啥呢?它為啥能提高執行效率呢?這篇我們來聊聊~
01 索引是什么?
索引是一種資料結構,它的出現就是為了提高資料查詢的效率,就像一本書的目錄,想想一本書幾百頁,沒有目錄估計找得夠嗆的,舉個通俗點的例子,我在知乎刷到的,比喻得很妙,
我們從小就用的新華字典,里面的聲母查詢方式就是聚簇索引, 偏旁部首就是二級索引 偏旁部首 + 筆畫就是聯合索引,
索引本身也是占用磁盤空間的(想想一本書中的目錄也是占用頁數的,你就知道了),它主要以檔案的形式存在于磁盤中,
1.1 索引的優缺點
優點
- 提高查詢陳述句的執行效率,減少 IO 操作的次數
- 創建唯一性索引,可以保證資料庫表中每一行資料的唯一性
- 加了索引的列會進行排序(一本書的章節順序不就是按照目錄來排嘛),在使用分組和排序子句進行查詢時,可以顯著減少查詢中分組和排序的時間
缺點
- 索引需要占物理空間
- 創建索引和維護索引要耗費時間,這種時間隨著資料量的增加而增加
- 當對表中的資料進行增刪改查是,索引也要動態的維護,這樣就降低了資料的更新效率
1.2 索引的分類
主鍵索引
一種特殊的唯一索引,不允許有空值,(主鍵約束 = 唯一索引 + 非空值)
唯一索引
索引列中的值必須是唯一的,但是允許為空值,
普通索引
MySQL 中的加索引型別,沒啥限制,允許空值和重復值,純粹為了提高查詢效率而存在,
單列索引
沒啥好說的,就是索引的列數量只有一個,每個表可以有多個單列索引,
組合索引
多列值組成一個索引,專門用于組合搜索,其效率大于索引合并,注意,使用它的時候需要遵守最左匹配原則,多個列作為查詢條件時,組合索引在作業中很常用,
全文索引
只能在文本內容,也就是 TEXT、CHAR、VARCHAR 資料型別的列上建全文索引,有人說創建單列索引不就完了嗎?考慮一種情況:當這列的內容很長時,用 like 查詢就會很慢,這是就適合建全文索引,
前綴索引
還是只能作用于文本內容,也就是 TEXT、CHAR、VARCHAR 資料型別的列上建前綴索引,它可以指定索引列的長度,它是這樣寫的:
// 在 x_test 的 x_name 列上創建一個長度為 4 的前綴索引
alter table x_test add index(x_name(4));
這個長度是根據實際情況來定的,長了太占用空間,短了不起效果,比如:我有個表的 x_name 的第一個字符幾乎都是一樣的(假設都是1),如果創建索引的長度 = 1,執行以下查詢的時候就可能比原來更糟,因為資料庫里面太多第一個字符 = 1 的列了,所以選的時候盡量選擇資料開始有差別的長度,
SELECT * FROM x_test WHERE x_name = '1892008.205824857823401.800099203178258.8904820949682635656.62526521254';
空間索引
MySQL 在 5.7 之后的版本支持了空間索引,而且支持 OpenGIS 幾何資料模型,MySQL 在空間索引這方面遵循 OpenGIS 幾何資料模型規則,
02 索引的記憶體模型
實作索引的方式有很多種,這里先介紹下最常見的三種:哈希表、有序陣列、二叉樹,其中二叉樹又分為二叉查找樹、平衡二叉樹、B 樹以及 B+ 樹,從而說明為啥 InnDB 選擇了 B+ 樹?為了方便作圖舉例我先建個表,建表陳述句如下:user 有兩列,一列是身份證號,還有一列是名稱,
CREATE TABLE IF NOT EXISTS `user`(
`id_card` INT(6) NOT NULL,
`name` VARCHAR(100) NOT NULL,
PRIMARY KEY ( `id_card` )
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
2.1 哈希表
HashMap 相信大家都用過,哈希表就是一種以鍵值對存盤資料的結構,在 MySQL 中 key 用于存盤索引列,value 就是某行的資料或者是它的磁盤地址,
用過 HashMap 的你可能知道了,當多個 key 經過哈希函式換算之后會出現同一個值,這種情況下就會 value 值的結構就是個鏈表,假設現在讓你通過身份證號找名字,這時它的哈希表索引結構是這樣的:

從上圖可知,user2 和 user4 哈希出來的 key 值都是 M,這個時候 value 的值就是個鏈表,如果你要查 id_card = 66688 的人,步驟是:先將 66688 通過哈希函式算出 M,然后按順序遍歷鏈表,找到 user2,
你可能注意到了上圖中四個 id_card 的值并不是遞增的,所以增加新 user 時速度會很快,往后追加就好,但又因為不是有序的,做區間查詢的速度就會很慢,
所以,哈希表結構適用于只有等值查詢的場景,不適合范圍查詢,
2.2 有序陣列
為了解決區間查詢速度慢的問題,有序陣列應運而生,它的等值和范圍查詢都很快,還是上面根據身份號找用戶的例子,這時候的索引結構是這樣的:

身份證號遞增且不重復從而有以上有序陣列,這是如果你要查 id_card = 66666 的用戶,用二分法就可以啦,復雜度是 O(log(N)),
這陣列還支持范圍查詢,還是用二分查找法,如果你要查區間 [12345,66666]的用戶,只需要二分查找出 id_card 大于等于 12345 且小于 66666 的用戶即可,
單看查詢效率,有序陣列簡直完美,但是如果我們要新增資料就很很難受了,假設你要新增 id_card = 12346 的用戶,那就只能把后面的資料都往后挪一個位置,成本太高了,
所以有序陣列只適用于存盤一些不怎么變的資料,比如一些過去的年份資料,
2.3 二叉搜索樹
二叉搜索樹,也稱二叉查找樹,或二叉排序樹,其定義也比較簡單,要么是一顆空樹,要么就是具有如下性質的二叉樹:每個節點只有兩個分叉,左子樹所有節點值比右子樹小,每個節點的左、右子樹也是一個小的二叉樹,且沒有健值相等的節點,
說概覽有點懵,先上個圖,一般的二叉搜索樹長這樣:

之所以設計成二叉有序的結構是因為可以利用二分查找法,它的插入和查找的時間復雜度都是 O(log(N)),但是最壞情況下,它的時間復雜度是 O(n),原因是在插入和洗掉的時候樹沒有保持平衡,比如順拐的二叉樹:

所以這種情況下,樹的查詢時間復雜度都變高,而且也不穩定,
2.4 平衡二叉樹
平衡二叉樹也叫 AVL 樹,它與二叉查找樹的區別在于平衡,它任意的左右子樹之間的高度差不大于 1**,我做了個對比,如下圖:

這樣就很開心了,根據平衡二叉樹的特點,它的查詢時間復雜度是 O(log(N)),當然為了維護平衡它更新的時間復雜度也是 O(log(N)),貌似完美?但是還有問題,
學過資料結構都知道,時間復雜度與樹高相關,你想想假設現在有一顆 100 萬節點的平衡二叉樹,樹高 20,一次查詢需要訪問 20 個資料塊,而根據計算機組成原理得知,從磁盤讀一個資料快平均需要 10ms 的尋址時間,PS:索引不止存在記憶體中,還會寫到磁盤上,所以優化的核心在于減少磁盤的 IO 次數,
也就是說,對于一個 100 萬行的表,如果使用平衡二叉樹來存盤,單獨訪問一行可能需要 20 個 10ms 的時間,也就是 0.2s,這很難受了,
此外,平衡二叉樹不支持快速的范圍查詢,范圍查詢時需要從根節點多次遍歷,查詢效率真心不高,
所以,大多數的資料庫存盤也并不使用平衡二叉樹,
2.5 B 樹
上面分析我們知道了,查詢慢是因為樹高,要多次訪問磁盤,為了讓一個查詢盡量少觸及磁盤,我們可以降低樹的高度,既然有二叉,那我們多分幾個叉,樹的高度不就降低了?所以,這時就用到了 B 樹(你心里沒點嗎?哈哈哈),
在 MySQL 的 InnoDB 存盤引擎一次 IO 會讀取的一頁(默認一頁 16K)的資料量,而二叉樹一次 IO 有效資料量只有 16 位元組,空間利用率極低,為了最大化利用一次 IO 空間,一個簡單的想法是在每個節點存盤多個元素,在每個節點盡可能多的存盤資料,每個節點可以存盤 1000 個索引(16k/16=1000),這樣就將二叉樹改造成了多叉樹,通過增加樹的叉樹,將樹從高瘦變為矮胖,構建 1 百萬條資料,樹的高度只需要 2 層就可以(1000*1000=1 百萬),也就是說只需要 2 次磁盤 IO 就可以查詢到資料,磁盤 IO 次數變少了,查詢資料的效率也就提高了,
B 樹也叫 B- 樹,一顆 m 階(m 表示這棵樹最多有多少個分叉)的 B 樹,特點是:
- 每個非葉子節點并且非根節點最少有 m/2 個(向上取整),即內部節點的子節點個數最少也有 m/2 個,
- 根節點至少有兩個子節點,每個內節點(非葉子節點就是內節點)最多有 m 個分叉,
- B 樹的所有節點都存盤資料,一個節點包含多個元素,比如健值和資料,節點中的健值從小到大排序,
- 葉子節點都在同一層,高度一致并且它們之間沒有指標相連,
3 階的 B 樹結構如下圖所示:

- 等值查詢
在這樣的結構下我們找值等于 48 的資料,還是使用二分查找法,它的查詢路徑是這樣的:資料庫1->資料塊3->資料塊9,一共經過三次磁盤 IO,而同樣資料量情況下,用平衡二叉樹存盤的樹高肯定是更高的,它的 IO 次數顯然是更高的,所以說 B 樹其實是加快了查詢效率,
- 范圍查詢
不知道大家注意到沒有? B 樹的葉子節點,并沒有指標相連,意味著如果是范圍查詢,比如我查 41~ 58 的資料,
首先,二分查找法訪問:資料塊1->資料塊3->資料塊9,找到 41;然后再回去從根節點遍歷:資料塊1->資料塊3->資料塊10,找到 58,一共經歷了 6 次 IO 查詢才算是完成,這樣查詢的效率就慢了很多,
它還存在以下問題:
1.葉子節點無指標相連,所以范圍查詢增加了磁盤 IO 次數,降低了查詢效率,
2.如果 data 存盤的是行記錄,行的大小隨著列數的增多,所占空間會變大,這時,一個頁中可存盤的資料量就會變少,樹相應就會變高,磁盤 IO 次數就會變大,
所以說,B 樹還有優化的空間,
2.6 B+ 樹
B+ 樹其實是從 B 樹衍生過來的,它與 B 樹有兩個區別:
- B+ 樹的非葉子節點不存放資料,只存放健值,
- B + 樹的葉子節點之間存在雙向指標相連,而且是雙向有序鏈表
它的資料結構如下圖所示:

由上圖得知,B+ 樹的資料都存放在葉子節點上,所以每次查詢我們都需要檢索到葉子節點才能把資料查出來,有人說了,那這不變慢了嗎?B 樹不一定要檢索到葉子節點呀,
其實不然,因為 B+ 的非葉子節點不再存盤資料,所以它可以存更多的索引,也即理論上 B+ 樹的樹高會比 B 樹更低,從這個角度來說,與其為了非葉子結點上能存盤值而選擇 B 樹,倒不如選擇 B+ 樹,降低樹高,
我們通過分析來看看 B+ 樹靠不靠譜,
- 等值查詢
在這樣的結構下我們找值等于 48 的資料,還是使用二分查找法,它的查詢路徑是這樣的:資料塊1->資料塊3->資料塊9,一共經過三次磁盤 IO,這沒毛病,
- 范圍查詢
比如我查 41~ 49 的資料,首先二分查找訪問:資料庫1->資料塊3->資料塊8,一樣經過了三次磁盤 IO,找到 41 快取到結果集,
但由于葉子節點是個雙向有序鏈表,這個時候只需要往后走,將 49 所在的資料塊 9 加載到記憶體遍歷,找到 49,查詢結束,只走了 4 次磁盤 IO,
這里可以看出對于范圍查詢來說,相比于 B 樹要走一遍老路,B+ 樹就顯得高效很多,
所以,B+ 樹中等值和范圍查詢都支持快速查,這樣 MySQL 就選擇了 B+ 樹作為索引的記憶體模型,
03 MySQL 的索引是如何執行的?
好了,可以作為所索引記憶體模型的資料結構都分析了一遍,最終 MySQL 還是選擇了 B+ 樹作為索引記憶體模型,那 B+ 樹在具體的引擎中是怎么發揮作用的呢?一起來看看
3.1 InnDB 索引
首先是 InnDB 索引,篇幅原因,我就聊聊主鍵索引和普通索引,
3.1.1 主鍵索引
主鍵索引又叫聚簇索引,它使用 B+ 樹構建,葉子節點存盤的是資料表的某一行資料,當表沒有創建主鍵索引是,InnDB 會自動創建一個 ROWID 欄位用于構建聚簇索引,規則如下:
- 在表上定義主鍵 PRIMARY KEY,InnoDB 將主鍵索參考作聚簇索引,
- 如果表沒有定義主鍵,InnoDB 會選擇第一個不為 NULL 的唯一索引列用作聚簇索引,
- 如果以上兩個都沒有,InnoDB 會使用一個 6 位元組長整型的隱式欄位 ROWID 欄位構建聚簇索引,該 ROWID 欄位會在插入新行時自動遞增,
多說無益,以下面的 Student 表為例,它的 id 是主鍵,age 列為普通索引,
CREATE TABLE `student` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`age` int(11) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
INDEX `index_age`(`age`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 66 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
表資料如下:


- 主鍵索引等值查詢 sql:
select * from student where id = 38;
程序如下:
- 第一次磁盤 IO:從根節點檢索,將資料塊 1 加載到記憶體,比較 38 < 44,走左邊,
- 第二次磁盤 IO:將左邊資料塊 2 加載到記憶體,比較 8<37<38,走右邊,
- 第三次磁盤 IO:將右邊資料塊 6 加載到記憶體,比較 37<38,38=38,查詢完畢,將資料回傳客戶端,
流程圖:3 次磁盤 IO

- 主鍵索引范圍查詢 sql
select * from student where id between 38 and 44;
前面也介紹說了,B+ 樹因為葉子節點有雙向指標,范圍查詢可以直接利用雙向有序鏈表,
程序如下:
- 第一次磁盤 IO:從根節點檢索,將資料塊 1 加載到記憶體,比較 38 < 44,走左邊,
- 第二次磁盤 IO:將左邊資料塊 2 加載到記憶體,比較 8<37<38,走右邊,
- 第三次磁盤 IO:將右邊資料塊 6 加載到記憶體,比較 37<38,38=38,走右邊,
- 第四次磁盤 IO:將右邊資料塊 7 加載到記憶體,比較 38<44=44,查詢完畢,將資料回傳客戶端,
流程圖:一共四次磁盤IO

3.1.2 普通索引
- 普通索引等值查詢 sql
在 InnDB 中,B+ 樹普通索引不存盤資料,只存盤資料的主鍵值,比如本表中的 age,它的索引結構就是這樣的:

執行以下查詢陳述句,它的流程又是怎樣的呢?
select * from student where age = 48;
使用普通索引需要檢索兩次索引,第一次檢索普通索引找出 age = 48 得到主鍵值,再使用主鍵到主鍵索引中檢索獲得資料,這個程序稱為回表,
也就是說,基于非主鍵索引的查詢需要多掃描一遍索引樹,因此,我們應該盡量使用主鍵查詢,
程序如下:
- 第一次磁盤 IO:從根節點檢索,將資料塊1 加載到記憶體,比較 48 < 54,走左邊,
- 第二次磁盤 IO:將左邊資料塊 2 加載到記憶體,比較 28<47<48,走右邊,
- 第三次磁盤 IO:將右邊資料塊 6 加載到記憶體,比較 47<48,48=48,得到主鍵 38,
- 第四次磁盤 IO:從根節點檢索,將根節點加載到記憶體,比較 38 < 44,走左邊,
- 第五次磁盤 IO:將左邊資料塊 2 加載到記憶體,比較 8<37<38,走右邊,
- 第六次磁盤 IO:將右邊資料塊 6 加載到記憶體,比較 37<38,38=38,查詢完畢,將資料回傳客戶端,
流程圖:一共 6 次磁盤 IO,

3.1.3 組合索引
如果為每一種查詢都設計一個索引,索引是不是太多了?如果我現在要根據學生的姓名去查它的年齡,假設這個需求出現的概覽很低,但我們也不能讓它走全表掃描吧?
但是為一個不頻繁的需求創建一個(姓名)索引是不是有點浪費了?那該咋做呢?我們可以建個(name,age)的聯合索引來解決呀,組合索引的結構如下圖所示:

執行以下查詢陳述句,它的流程又是怎樣的呢?
select * from student where name = '二狗5' and age = 48;
程序如下:
- 第一次磁盤 IO:從根節點檢索,將資料塊1 加載到記憶體,比較 二狗5 < 二狗6,走左邊,
- 第二次磁盤 IO:將左邊資料塊 2 加載到記憶體,比較 二狗2<二狗4<二狗5,走右邊,
- 第三次磁盤 IO:將右邊資料塊 6 加載到記憶體,比較 二狗4<二狗5,二狗5=二狗5,得到主鍵 38,
- 第四次磁盤 IO:從根節點檢索,將根節點加載到記憶體,比較 38 < 44,走左邊,
- 第五次磁盤 IO:將左邊資料塊 2 加載到記憶體,比較 8<37<38,走右邊,
- 第六次磁盤 IO:將右邊資料塊 6 加載到記憶體,比較 37<38,38=38,查詢完畢,將資料回傳客戶端,
流程圖:一共六次磁盤 IO

3.1.4 最左匹配原則
最左前綴匹配原則和聯合索引的索引存盤結構和檢索方式是有關系的,
在組合索引樹中,最底層的葉子節點按照第一列 name 列從左到右遞增排列,但是 age 列是無序的,age 列只有在 name 列值相等的情況下小范圍內遞增有序,
就像上面的查詢,B+ 樹會先比較 name 列來確定下一步應該搜索的方向,往左還是往右,如果 name 列相同再比較 age 列,但是如果查詢條件沒有 name 列,B + 樹就不知道第一步應該從哪個節點查起,這就是所謂的最左匹配原則,
可以說創建的 idx_name_age (name,age) 索引,相當于創建了 (name)、(name,age)兩個索引,、
組合索引的最左前綴匹配原則:使用組合索引查詢時,mysql 會一直向右匹配直至遇到范圍查詢 (>、<、between、like) 就停止匹配,
3.1.5 覆寫索引
覆寫索引是一種很常用的優化手段,因為在上面普通索引的例子中,由于查詢結果所需要的資料只在主鍵索引上有,所以不得不回表,那么有沒有可能經過索引優化,避免回表呢?比如改成這樣子:
select age from student where age = 48;
在上面普通索引例子中,如果我只需要 age 欄位,那是不是意味著我們查詢到普通索引的葉子節點就可以直接回傳了,而不需要回表,這種情況就是覆寫索引,
看下執行計劃:
覆寫索引的情況:

未覆寫索引的情況:

3.2 myisam 索引
還是上面那張 student 表,建表陳述句:
CREATE TABLE `student` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`age` int(11) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
INDEX `index_age`(`age`) USING BTREE
) ENGINE = MyISAM AUTO_INCREMENT = 66 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
3.2.1 主鍵索引
與 InnDB 不同的是 myisam 的資料檔案和索引檔案是分開存盤的,它的葉子節點存的是健值,資料是索引所在行的磁盤地址,它的結構如下:表 student 的索引檔案存放在 student.MYI 中,資料檔案存盤在 student.MYD 中,

- 主鍵索引等值查詢
select * from student where id = 38;
它的具體執行流程如下:
- 第一次磁盤 IO:從根節點檢索,將資料塊 1 加載到記憶體,比較 38 < 44,走左邊,
- 第二次磁盤 IO:將左邊資料塊 2 加載到記憶體,比較 8<37<38,走右邊,
- 第三次磁盤 IO:將右邊資料塊 6 加載到記憶體,比較 37<38,38=38,得到索引所在行的記憶體地址,
- 第四次磁盤 IO:根據地址到資料檔案 student.MYD 中獲取對應的行記錄,
流程圖:一共 4 次磁盤 IO,

- 主鍵索引范圍查詢
select * from student where id between 38 and 44;
程序如下:
- 第一次磁盤 IO:從根節點檢索,將資料塊 1 加載到記憶體,比較 38 < 44,走左邊,
- 第二次磁盤 IO:將左邊資料塊 2 加載到記憶體,比較 8<37<38,走右邊,
- 第三次磁盤 IO:將右邊資料塊 6 加載到記憶體,比較 37<38,38=38,得到索引所在行的記憶體地址,
- 第四次磁盤 IO:根據地址到資料檔案 student.MYD 中獲取主鍵 38 對應的行記錄,
- 第五次磁盤 IO:將右邊資料塊 7 加載到記憶體,比較 38<44=44,得到索引所在行的記憶體地址,
- 第六次磁盤 IO:根據地址到資料檔案 student.MYD 中獲取主鍵 44 對應的行記錄,
3.2.2 普通索引
在 MyISAM 中,輔助索引和主鍵索引的結構是一樣的,沒有任何區別,葉子節點的資料存盤的都是行記錄的磁盤地址,只是主鍵索引的鍵值是唯一的,而輔助索引的鍵值可以重復,
查詢資料時,由于輔助索引的鍵值不唯一,可能存在多個擁有相同的記錄,所以即使是等值查詢,也需要按照范圍查詢的方式在輔助索引樹中檢索資料,
3.3 索引的使用技巧
3.3.1 避免回表
上面說了,回表的原因是因為查詢結果所需要的資料只在主鍵索引上有,所以不得不回表,回表必然會影響性能,那怎么避免呢?
使用覆寫索引,舉個栗子:還是上面的 student ,它的一條 sql 在業務上很常用:
select id, name, age from student where name = '二狗2';
而 student 表的其他欄位使用頻率遠低于它,在這種情況下,如果我們在建立 name 欄位的索引的時候,并不是使用單一索引,而是使用聯合索引(name,age)這樣的話再執行這個查詢陳述句就可以根據輔助索引查詢到的結果獲取當前陳述句的完整資料,
這樣就有效避免了通過回表再獲取 age 的資料,喏,這就是一個典型的用覆寫索引的優化策略減少回表的情況,
3.3.2 聯合索引的使用
聯合索引,在建立索引的時候,盡量在多個單列索引上判斷下是否可以使用聯合索引,聯合索引的使用不僅可以節省空間,還可以更容易的使用到索引覆寫,比如上面的 student 表,我就建了 (name,age) 和 age 索引,
聯合索引的創建原則,在創建聯合索引的時候因該把頻繁使用的列、區分度高的列放在前面,頻繁使用代表索引利用率高,區分度高代表篩選粒度大,
也可以在常需要作為查詢回傳的欄位上增加到聯合索引中,如果在聯合索引上增加一個欄位而使用到了覆寫索引,這種情況下應該使用聯合索引,
聯合索引的使用
- 考慮當前是否已經存在多個可以合并的單列索引,如果有,那么將當前多個單列索引創建為一個聯合索引,
- 當前索引存在頻繁使用作為回傳欄位的列,這個時候就可以考慮當前列是否可以加入到當前已經存在索引上,使其查詢陳述句可以使用到覆寫索引,
3.3.3 索引下推
現在我的表資料是這樣的:加了一個 sex 列,

說到滿足最左前綴原則的時候,最左前綴可以用于在索引中定位記錄,這時,你可能要問,那些不符合最左前綴的部分,會怎么樣呢?
我們還是以學生表的聯合索引(name,age)為例,如果現在有一個需求:檢索出表中“名字第一個字是二,而且年齡是 38 歲的所有男生”,那么,SQL陳述句是這么寫的:
select * from student where name like '張%' and age=38 and sex='男';
根據前綴索引規則,所以這個陳述句在搜索索引樹的時候,只能用"張",找到三個滿足條件的記錄(圖中紅框資料),當然,這還不錯,總比全表掃描要好,
然后呢?當然是判斷其他條件是否滿足,
在MySQL5.6之前,只能從滿足條件的記錄 id=18 開始一個個回表,到主鍵索引上找出資料行,再對比欄位
而MySQL 5.6引入的索引下推優化(index condition pushdown),可以在索引遍歷程序中,對索引中包含的欄位先做判斷,直接過濾掉不滿足條件的記錄,減少回表次數,
它的整個執行的流程圖是這樣的:

InnoDB在(name,age)索引內部就判斷了 age 是否等于 38,對于不等于 38 的記錄,直接判斷并跳過,在我們的這個例子中,只需要對 id=18 和 id=65 這兩條記錄回表取資料判斷,就只需要回表 2 次,這就是所謂的索引下推,
巨人的肩膀
- 《高性能MySQL》
- zhuanlan.zhihu.com/p/142361798
- cnblogs.com/lianzhilei/p/11250589.html
- blog.csdn.net/qq_35190492/article/details/109257302
- time.geekbang.org/column/article/69636
- cnblogs.com/happyflyingpig/p/7662881.html
- tech.meituan.com/2014/06/30/mysql-index.html
總結
本文講解了索引是什么?它的優缺點、分類;索引的 6 種記憶體模型;為什么使用 B+ 樹?InnDB 和 MyIsam 引擎的主鍵索引、普通索引、組合索引、覆寫索引都是怎么起作用的?最左匹配原則是啥?最后還聊了下使用索引的小技巧,可以說,索引相關的知識點都在這了,看完這一篇還不懂的話,你來捶我呀!
好啦,以上就是狗哥關于索引的總結,感謝各技術社區大佬們的付出,如果說我看得更遠,那是因為我站在你們的肩膀上,希望這篇文章對你有幫助,我們下篇文章見~
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/270992.html
標籤:MySQL
上一篇:聊聊 Explain 執行計劃
