MySQL的索引是怎麼加速查詢的?

MySQL 的索引長什麼樣子?索引究竟是怎麼加速查詢的?html

事實上,在你尚未執行 create index 語句的時候,MySQL 就已經建立索引了。mysql

讓咱們從建表開始吧。sql

聚簇索引

執行建表語句:數據庫

CREATE TABLE `student` (
  `id` BIGINT UNSIGNED AUTO_INCREMENT NOT NULL COMMENT '主鍵id',
  `student_no` VARCHAR(64) COMMENT '學號',
  `name` VARCHAR(64) COMMENT '學生姓名',
  `age` INT COMMENT '學生年齡',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB CHARSET=utf8mb4 COMMENT='學生信息表';
複製代碼

插入 5 條數據:數組

insert into student(student_no,name,age) values(101,"Alice",18);
insert into student(student_no,name,age) values(102,"Bob",19);
insert into student(student_no,name,age) values(104,"Brandt",15);
insert into student(student_no,name,age) values(105,"David",19);
insert into student(student_no,name,age) values(109,"David",18);
複製代碼

在插入的過程當中,MySQL 會用你指定的主鍵,在這裏是遞增主鍵,維護起一棵 B+樹,我用了舊金山大學作的 BPlusTree Visualization 來模擬這棵樹的樣子,主鍵從 1 開始遞增,插入五條,因此是 1 到 5: 微信

若是有時間,也建議你到這個網站去,從 1 到 5,一個一個插入,你會看到 B+樹在插入的過程當中是怎麼維護它的幾個特性的:

  • 有序:左邊節點比右邊小
  • 自平衡:左右兩邊數量趨於相等
  • 節點分裂:節點在遇到元素數量超過節點容量時,是如何分裂成兩個的,這個也是 MySQL 頁分裂的原理
  • ......

插句題外話,MySQL 裏絕大多數索引都是 B+樹,另外有少數狀況會使用 Hash索引、R-tree等等,今天只討論 B+樹。數據結構

模擬工具只支持插入一個值,因此你看不到主鍵以外的其餘數據,實際上,這棵 B+樹的葉子節點是帶有行的所有數據的,因此我又本身畫了張完整的圖: 工具

若是沒有這棵 B+樹,你要根據主鍵查詢,好比

select * from student where id = 5;
複製代碼

對不起,數據是無序的,你只能全表掃描,猶如大浪淘沙。性能

有同窗會說主鍵不是遞增的嗎,那不就能夠用二分法來查找?不是的,主鍵雖然是遞增的,可是若是你寫入磁盤時,沒有去維護有序數組這樣一個數據結構(好比你刪掉了 4,怎麼把 5 往前面挪),那數據在磁盤裏依舊是無序的,查找時只能隨機查找,而若是你維護了有序數組這樣的數據結構,其實也是建了索引,只是建了不同的數據結構的索引罷了。大數據

至於爲何 MySQL 選擇了 B+樹,而不用上面說的有序數組、hash索引等,我們後面再聊。

如今有了這棵 B+樹,數據被有規律的存儲起來,查找 id=5,也再也不大浪淘沙,而是變得頗有章法:

  • 從上到下,先找到 3,5 比它大,找右節點
  • 接着找到 4,發現 5 仍是比它大,繼續找右節點
  • 此次到達葉子節點了,葉子節點是一個遞增的數組,那就用二分法,找到 id=5 的數據

**你要訪問磁盤的次數,是由這棵樹的層數決定的。**爲了方便說明,我在文章裏舉的例子的數據量不會太大,因此用不用索引,性能提高的效果不明顯,可是你能夠腦補下大數據量的畫面。

若是你沒有指定主鍵呢?不要緊,惟一鍵也能夠。

連惟一鍵也沒有?也不要緊,mysql會給你建一個rowid字段,用它來組織這棵 B+樹.

反正 MySQL 就一個目的,數據要有規律的存儲起來,數據是否被規律的管理起來,是數據庫和文件系統區分開來的重要因素。

這個 MySQL 不管如何都會建起來,而且存儲有完整行數據的索引,就叫聚簇索引(clustered index)。

二級索引

聚簇索引只能幫你加快主鍵查詢,可是若是你想根據姓名查詢呢?

對不起,看看上面這棵樹你就知道,數據並無按照姓名進行組織,因此,你仍是隻能全表掃描。

不想全表掃描,怎麼辦?那就給姓名字段也加個索引,讓數據按照姓名有規律的進行組織:

create index idx_name on student(name);
複製代碼

這時候 MySQL 又會建一棵新的 B+樹:

你會發現這棵樹的葉子節點,只有姓名和主鍵ID兩個字段,沒有行的完整數據,這時候你執行:

select * from student where name = "David";
複製代碼

MySQL 到你剛剛建立的這棵 B+樹 查詢,快速查到有兩條姓名是「David」的記錄,而且拿到它們的主鍵,分別是 4 和 5,可是你要的是select *呀,怎麼辦?

別忘了,MySQL 在一開始就給你建了一棵 B+樹 了,把這兩棵樹,放在一塊兒,拿着從這棵樹上查到的兩個主鍵ID,去聚簇索引找,事情不就解決了?

這個不帶行數據完整信息的索引,就叫二級索引(secondary index),也叫輔助索引。

複合索引

繼續,若是我還想根據姓名和年齡同時查詢呢?

select * from student where name = "David" and age = 18;
複製代碼

仍是那個道理,數據雖然按照 name 有規律的組織了,可是沒有按照 age 有規律組織,因此咱們要給 nameage同時建索引:

create index idx_name_age on student(name,age);
複製代碼

這時候 MySQL 又會建一棵 B+樹,這下 B+樹 的節點裏面,不僅有 name,還有 age 了:

注意觀察我用紅色虛線框出來的那兩個節點,這是這棵樹和上面那棵只給 name 建索引的樹的惟一區別,兩個元素換了個位,由於排序時,是先用 name 比較大小,若是 name 相同,則用 age 比較

仍是那句話,這裏舉的例子數據量不多,你能夠想象下有一萬個叫「David」的學生,年齡隨機分佈在 13 到 20 之間,這時候若是沒有按照 age 進行有規律的存儲,你仍是得掃描一萬行數據。

未完待續

寫到這,我想起以前大學的一個學霸,人家考高數前都在背公式,他卻在紙上練習這些公式的推導過程,紙上寫的密密麻麻,當時不解,如今回想起來,這實在是降維打擊。

別人都只會用公式,他卻時刻牢記這些公式是怎麼來的,別人考試就只會套用公式,他卻能夠用這些公式之外的知識解決問題。

MySQL 索引也是,不少人都知道索引就像字典的目錄,索引是 B+樹,可是若是隻知道這些,又有什麼用呢?

知識是須要往深裏學,才能轉化爲能力的,你知道的多,並不表明你能解決的問題就多,反而那些知道的沒那麼多,可是對他知道的東西,都研究透徹的人,才能一通百通。

當你知道了 MySQL 的索引長成這個樣子後,還用去背什麼「最左匹配」嗎?

隨便問個問題,只給 student 表建 idx_name_age 這個複合索引,這兩個 sql 語句,會走索引嗎?

select * from student where name = "David";
複製代碼
select * from student where age = 18;
複製代碼

照着上面這幾張圖,你幾乎能夠推導出一切,什麼樣的 sql 能走索引,什麼樣的 sql 不能。

甚至,這麼精妙的數據結構設計,難道就只能用來加速查詢嗎?

至少如今我能想到的,索引能夠拿來乾的事情,就至少有四種。

下次聊。

(吐血畫圖,此處應該點贊)

ps: 在掘金髮的第一篇文章,沒啥,就是想讓文章讓更多優秀讀者看到。

參考

微信搜:柳樹的絮叨叨

我沉澱內容,你沉澱能力。

相關文章
相關標籤/搜索