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: 微信
插句題外話,MySQL 裏絕大多數索引都是 B+樹,另外有少數狀況會使用 Hash索引、R-tree等等,今天只討論 B+樹。數據結構
模擬工具只支持插入一個值,因此你看不到主鍵以外的其餘數據,實際上,這棵 B+樹的葉子節點是帶有行的所有數據的,因此我又本身畫了張完整的圖: 工具
select * from student where id = 5;
複製代碼
對不起,數據是無序的,你只能全表掃描,猶如大浪淘沙。性能
有同窗會說主鍵不是遞增的嗎,那不就能夠用二分法來查找?不是的,主鍵雖然是遞增的,可是若是你寫入磁盤時,沒有去維護有序數組這樣一個數據結構(好比你刪掉了 4,怎麼把 5 往前面挪),那數據在磁盤裏依舊是無序的,查找時只能隨機查找,而若是你維護了有序數組這樣的數據結構,其實也是建了索引,只是建了不同的數據結構的索引罷了。大數據
至於爲何 MySQL 選擇了 B+樹,而不用上面說的有序數組、hash索引等,我們後面再聊。
如今有了這棵 B+樹,數據被有規律的存儲起來,查找 id=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 有規律組織,因此咱們要給 name
和 age
同時建索引:
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: 在掘金髮的第一篇文章,沒啥,就是想讓文章讓更多優秀讀者看到。
參考
微信搜:柳樹的絮叨叨
我沉澱內容,你沉澱能力。