什么是聚簇索引和非聚簇索引完疫,如何理解回表、索引下推
聚簇索引(Clustered Index)和非聚簇索引(Non-clustered Index)是數(shù)據(jù)庫中的兩種索引類型债蓝,它們在組織和存儲數(shù)據(jù)時有不同的方式壳鹤。
聚簇索引
聚簇索引簡單理解就是將數(shù)據(jù)與索引放在一起,找到索引即找到了數(shù)據(jù)饰迹。換句話說芳誓,對于聚簇索引,其非葉子節(jié)點上存儲的是索引字段的值啊鸭,而葉子節(jié)點上存儲的是對應(yīng)記錄的整行數(shù)據(jù)锹淌。
在 InnoDB 中,聚簇索引(Clustered Index)是指按照每張表的主鍵構(gòu)建的一種索引方式赠制。它將表數(shù)據(jù)按照主鍵的順序存儲在磁盤上赂摆,確保了行的物理存儲順序與主鍵的邏輯順序相同。這種索引方式使得查找聚簇索引的速度非持有快烟号。
非聚簇索引是指將索引與數(shù)據(jù)分開存儲的一種方式。在非聚簇索引中政恍,葉子節(jié)點包含索引字段的值以及指向數(shù)據(jù)頁數(shù)據(jù)行的邏輯指針汪拥。
在 InnoDB 中,非聚簇索引(Non-clustered Index)是根據(jù)非主鍵字段創(chuàng)建的索引篙耗,通常稱為二級索引迫筑。它不影響表中數(shù)據(jù)的物理存儲順序,而是單獨創(chuàng)建一張索引表宗弯,用于存儲索引列和對應(yīng)行的指針脯燃。
在 InnoDB 中,主鍵索引就是聚簇索引蒙保,而非主鍵索引則是非聚簇索引曲伊。因此,在 InnoDB 中:
對于聚簇索引追他,其非葉子節(jié)點上存儲的是索引值坟募,而葉子節(jié)點上存儲的是整行記錄。
對于非聚簇索引邑狸,其非葉子節(jié)點上存儲的是索引值懈糯,而葉子節(jié)點上存儲的是主鍵的值以及索引值。
因此单雾,通過非聚簇索引進(jìn)行查詢時赚哗,需要進(jìn)行一次回表操作她紫,即先通過索引查找到主鍵 ID,然后再通過 ID 查詢所需字段屿储。
沒有創(chuàng)建主鍵怎么辦贿讹?
在 InnoDB 中,如果表結(jié)構(gòu)中沒有定義主鍵够掠,數(shù)據(jù)庫會自動為每行記錄添加一個隱藏的主鍵民褂,通常稱為 db_row_id 字段。這個隱藏主鍵會確保每行記錄都有一個唯一的標(biāo)識符疯潭。
如果表中沒有合適的唯一索引可用作聚簇索引赊堪,數(shù)據(jù)庫會使用這個隱藏主鍵來構(gòu)建聚簇索引。這樣可以確保每行記錄都有一個物理上的唯一標(biāo)識符竖哩,并且能夠保持索引的唯一性和快速查詢的特性哭廉。
什么是回表,怎么減少回表的次數(shù)相叁?
在 InnoDB 中遵绰,索引 B+樹的葉子節(jié)點存儲了整行數(shù)據(jù)的是主鍵索引,也被稱為聚簇索引增淹。而索引 B+樹的葉子節(jié)點存儲了主鍵的值的是非主鍵索引街立,也被稱為非聚簇索引。
在數(shù)據(jù)存儲方面埠通,主鍵(聚簇)索引的 B+樹的葉子節(jié)點直接包含了我們要查詢的整行數(shù)據(jù)赎离。而非主鍵(非聚簇)索引的葉子節(jié)點則包含了主鍵的值。
因此端辱,當(dāng)我們通過非聚簇索引進(jìn)行查詢時梁剔,首先會通過非聚簇索引查找到主鍵的值,然后需要再通過主鍵的值進(jìn)行一次查詢才能獲取到我們要查詢的數(shù)據(jù)舞蔽。這個過程稱為回表荣病。
因此,在 InnoDB 中渗柿,使用主鍵進(jìn)行查詢效率更高个盆,因為這個過程不需要回表。此外朵栖,通過依賴覆蓋索引颊亮、索引下推等技術(shù),我們可以通過優(yōu)化索引結(jié)構(gòu)和 SQL 語句來減少回表的次數(shù)陨溅。
什么是索引覆蓋终惑、索引下推?
覆蓋索引
覆蓋索引是指查詢語句的執(zhí)行只需從索引中獲取所需數(shù)據(jù)门扇,而無需從數(shù)據(jù)表中讀取雹有。也可以稱之為實現(xiàn)了索引覆蓋偿渡。
當(dāng)一條查詢語句符合覆蓋索引條件時,MySQL 只需通過索引就能返回查詢所需數(shù)據(jù)霸奕,而不需要進(jìn)行索引查找后再返回表操作溜宽,從而減少 I/O,提高效率质帅。
例如适揉,在表 covering_index_sample 中有一個普通索引 idx_key1_key2(key1,key2)。
當(dāng)我們執(zhí)行以下 SQL 語句時:
javascript
SELECT key2 FROM covering_index_sample WHERE key1 = 'keytest';
此時可以通過覆蓋索引查詢临梗,無需進(jìn)行回表操作涡扼。
但是對于以下 SQL 語句稼跳,雖然是索引覆蓋盟庞,但由于不符合最左前綴匹配,無法利用索引(會掃描索引樹):
javascript
SELECT key1 FROM covering_index_sample WHERE key2 = 'keytest';
另外汤善,如果查詢語句中需要的信息不包含在聯(lián)合索引中什猖,那么就無法使用索引覆蓋。例如:
javascript
SELECT key2, key3 FROM covering_index_sample WHERE key1 = 'keytest';
索引下推
索引下推是 MySQL 5.6 引入的一種優(yōu)化技術(shù)红淡,默認(rèn)開啟不狮,可通過設(shè)置 SET optimizer_switch = 'index_condition_pushdown=off'; 來關(guān)閉。
它的工作原理如下:假設(shè) people 表中(zipcode在旱,lastname摇零,firstname)構(gòu)成一個索引⊥靶考慮以下查詢:
javascript
SELECT * FROM people WHERE zipcode='95054' AND lastname LIKE '%etrunia%' AND address LIKE '%Main Street%';
如果沒有使用索引下推技術(shù)驻仅,MySQL 會通過 zipcode='95054'從存儲引擎中查詢對應(yīng)的數(shù)據(jù),然后將結(jié)果返回到 MySQL 服務(wù)端登渣,接著 MySQL 服務(wù)端再基于lastname LIKE '%etrunia%' 和 address LIKE '%Main Street%'來判斷數(shù)據(jù)是否符合條件噪服。
而如果使用了索引下推技術(shù),MySQL 首先會返回符合 zipcode='95054'的索引胜茧,然后根據(jù)lastname LIKE '%etrunia%'來判斷索引是否符合條件粘优。如果符合條件,則根據(jù)該索引定位對應(yīng)的數(shù)據(jù)呻顽;如果不符合雹顺,則直接拒絕。有了索引下推優(yōu)化廊遍,可以在有 like 條件查詢的情況下无拗,減少回表次數(shù)。
當(dāng)一條 SQL 使用到索引下推時昧碉,執(zhí)行計劃中的 extra 字段的內(nèi)容會顯示為 "Using index condition"英染。
索引下推不止 like
上面的例子中揽惹,提到了 like,包括 MySQL 官網(wǎng)中也只提到了 like四康,但是其實不止有 Like搪搏。因為我認(rèn)為索引下推其實是解決索引失效帶來的效率低的問題的一種手段。
所以當(dāng)聯(lián)合索引中闪金,某個非前導(dǎo)列因為索引失效而要進(jìn)行掃表并回表時疯溺,就可以進(jìn)行索引下推優(yōu)化了。