《MySQL實戰45講》學習筆記4——MySQL中InnoDB的索引

索引是在存儲引擎層實現的,且在 MySQL 不一樣存儲引擎中的實現也不一樣,本篇文章介紹的是 MySQLInnoDB 的索引。sql

下文將以這張表爲例開展。緩存

# 建立一個主鍵爲 id 的表,表中有字段 k,而且在 k 上有索引。
create table T(
  `id` int(11) AUTO_INCREMENT, 
  `k` int(11) NOT NULL, 
  `name` varchar(16),
  `age` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY (`k`),
  KEY `name_age` (`name`,`age`)
) ENGINE=InnoDB;

# 插入多條數據
insert into T values (100, 'Bob'),(200, 'Peter'),(300,'Mary');

1、InnoDB索引模型

InnoDB 中,表都是根據主鍵順序以索引的形式存放的,也就是數據放在主鍵索引上,其餘索引上保存的是主鍵 id,這種存儲方式的表稱爲索引組織表性能

InnoDB 使用了 B+樹 索引模型,因此數據都是存儲在 B+樹 中的。每個索引在 InnoDB 裏面對應一棵 B+樹優化

2、索引的類型

2.1 主鍵約束:主鍵索引和二級索引

主鍵索引的葉子節點存的是整行數據。在 InnoDB 裏,主鍵索引也被稱爲聚簇索引code

非主鍵索引的葉子節點內容是主鍵的值。在 InnoDB 裏,非主鍵索引也被稱爲二級索引輔助索引
blog

2.1.1 主鍵索引和非主鍵索引的區別

主鍵查詢方式:只須要搜索 ID 這棵 B+樹
普通索引查詢方式:先搜索普通索引的 B+樹,獲得主鍵索引 ID 的值,再到 ID 索引樹上搜索,這個過程稱爲回表排序

2.1.2 主鍵索引的選取規則

從空間的角度出發:主鍵列長度儘量短,每一個二級索引的葉子節點是主鍵,主鍵過長會致使二級索引佔用空間更大。
從性能的角度出發:推薦使用自增索引,非自增主鍵在插入和刪除的操做中,會致使頁分裂和頁合併。索引

2.1.3 非主鍵索引的優化:覆蓋索引

先看下面這個 sql內存

select * from T where k = 100;

這個 sql 語句會在 k 索引樹上找到 k=100 的記錄,取得 ID=15
再到 ID 索引樹查到 ID=15 對應的記錄,發生了回表,若是將 sql 語句改成it

select id from T where k = 100;

由於 ID 的值已經在 k 索引樹上了,所以能夠直接提供查詢結果,不須要回表。

若是一個索引包含(或覆蓋)全部須要查詢的字段的值,稱爲覆蓋索引,即只需掃描索引而無須回表。

2.1.4 業務字段作主鍵的條件

若是不使用自增 ID 作主鍵,用業務字段直接作主鍵,則須要知足:只有一個索引,且該索引爲惟一索引。

因爲沒有其餘索引,因此不用考慮其餘索引的葉子節點大小的問題,把這個索引設置爲主鍵,避免每次查詢須要搜索兩棵樹。

2.1.5 索引的重建
  • 主鍵索引的重建
# 正確作法
alter table T engine=InnoDB

# 錯誤作法
alter table T drop primary key;
alter table T add primary key(id);

直接刪掉主鍵索引會使得全部的二級索引都失效,而且會用 ROWID 來做主鍵索引。

  • 非主鍵索引的重建
alter table T drop index k;
alter table T add index(k);

索引可能由於刪除,或者頁分裂等緣由,致使數據頁有空洞,重建索引的過程會建立一個新的索引,把數據按順序插入,這樣頁面的利用率最高,達到省空間的目的。

2.2 索引字段數量:聯合索引和單列索引

在上面的建表語句中,能夠看到有兩個索引,一個是 k 索引,一個是name_age 索引,不難看出,前者是單列索引,然後者就是聯合索引了。

爲何會有聯合索引呢?當查詢條件爲2個及以上時,好比當常常要用 nameage 去查詢數據時:

select * from T where name = 'Job' and age = 28;

建立一個 (name,age) 的聯合索引,至關於建立了 namename、age 這兩個組合的索引,能夠加速檢索。

2.2.1 最左前綴原則

顧名思義是最左優先,以最左邊的爲起點任何連續的索引都能匹配上。

聯合索引的示例圖以下:

索引項是按照索引定義裏的字段順序來排序的,所以在建立聯合索引時,要根據業務需求,where子句中使用最頻繁的一列放在最左邊

當已經有了 (a,b) 這個聯合索引後,通常就不須要單獨在 a 上創建索引了。所以,第一原則是,若是經過調整順序,能夠少維護一個索引,那麼這個順序每每就是須要優先考慮採用的。

當建立 (a,b,c) 聯合索引時,至關於建立了 (a) 單列索引、(a,b) 聯合索引以及 (a,b,c) 聯合索引。
想要索引生效的話,只能使用 aa,ba,b,c 三種組合;a,c 組合也能夠,但實際上只用到了 a 的索引,並無用到 c

2.2.2 索引下推

MySQL 5.6 引入索引下推優化(index condition pushdown),能夠在索引遍歷過程當中,對索引中包含的字段先作判斷,直接過濾掉不知足條件的記錄,減小回表次數。

好比根據(name,age)聯合索引查詢全部知足名稱以「張」開頭的索引,而後直接再篩選出年齡小於等於10的索引,以後再回表查詢全行數據。

注意:innodb 引擎的表,索引下推只能用於二級索引。

2.3 惟一約束:惟一索引和普通索引

普通索引容許被索引的數據列包含重複的值,建立惟一索引的目的通常不是爲了提升訪問速度,而只是爲了不數據重複。

2.3.1 change buffer機制

當須要更新一個數據頁時,若是數據頁在內存中就直接更新,而若是這個數據頁尚未在內存中的話,在不影響數據一致性的前提下,InnoDB 會將這些更新操做緩存在 change buffer 中,這樣就不須要從磁盤中讀入這個數據頁了。在下次查詢須要訪問這個數據頁的時候,將數據頁讀入內存,而後執行 change buffer 中與這個頁有關的操做。經過這種方式就能保證這個數據邏輯的正確性。

2.3.2 惟一索引和普通索引的選擇

不推薦使用惟一索引,這是由於:

從查詢的角度出發:

  • 若是查詢結果全在內存上:惟一索引在數據頁中查找知足查詢條件的第一條記錄便可返回;普通索引須要再獲取下一條記錄,因爲索引項是有序的且內存操做,多一次判斷的時間損耗可忽略不計;
  • 若是查詢結果不在內存上:先把數據頁加載到內存中,再按照查詢結果全在內存的流程處理。

從更新的角度出發:

  • 若是須要更新的記錄全在內存上,直接更新內存記錄並返回;
  • 若是須要更新的記錄不在內存上以及部分在內存上:惟一索引須要先將須要更新的記錄從磁盤中加載到內存,更新內存記錄並寫 redolog;普通索引將更新操做寫入 change buffer,通知執行器更新完成;在下次讀相關記錄的時候,先把原記錄讀取到內存,再將 change buffer 上的操做在內存記錄上回放,並寫 redolog
  • 普通索引在更新時,節省了更新時從磁盤讀取記錄的時間,而惟一索引在更新時,若記錄不在內存,須要從磁盤讀取記錄到內存。

結論:change buffer 只適用於普通索引,而不適用於惟一索引。

後記

關於索引的知識點比較多,但每看一遍專欄都會有新的收穫。fighting!💪

相關文章
相關標籤/搜索