了解一下MySQL中的回表查詢與索引覆蓋饱溢。
回表查詢
要說回表查詢酪捡,先要從InnoDB的索引實現(xiàn)說起。InnoDB有兩大類索引码泞,一類是聚集索引(Clustered Index),一類是普通索引(Secondary Index)狼犯。
InnoDB的聚集索引
InnoDB聚集索引的葉子節(jié)點存儲行記錄余寥,因此InnoDB必須要有且只有一個聚集索引。
1.如果表定義了PK(Primary Key悯森,主鍵)宋舷,那么PK就是聚集索引。
2.如果表沒有定義PK瓢姻,則第一個NOT NULL UNIQUE的列就是聚集索引祝蝠。
3.否則InnoDB會另外創(chuàng)建一個隱藏的ROWID作為聚集索引。
這種機制使得基于PK的查詢速度非常快绎狭,因為直接定位的行記錄细溅。
InnoDB的普通索引
InnoDB普通索引的葉子節(jié)點存儲主鍵值(MyISAM則是存儲的行記錄頭指針)。
什么是回表查詢
假設(shè)有個t表(id PK, name KEY, sex, flag)儡嘶,這里的id是聚集索引喇聊,name則是普通索引。
表中有四條記錄:
idnamesexflag
1sjmA
3zsmA
5lsmA
9wwfB
聚集索引的B+樹索引(id是PK蹦狂,葉子節(jié)點存儲行記錄):
普通索引的B+樹索引(name是KEY承疲,葉子節(jié)點存儲PK值,即id):
普通索引因為無法直接定位行記錄鸥咖,其查詢過程在通常情況下是需要掃描兩遍索引樹的燕鸽。
select*fromtwherename='lisi';
這里的執(zhí)行過程是這樣的:
粉紅色的路徑需要掃描兩遍索引樹,第一遍先通過普通索引定位到主鍵值id=5啼辣,然后第二遍再通過聚集索引定位到具體行記錄啊研。這就是所謂的回表查詢,即先定位主鍵值鸥拧,再根據(jù)主鍵值定位行記錄党远,性能相對于只掃描一遍聚集索引樹的性能要低一些。
索引覆蓋
索引覆蓋是一種避免回表查詢的優(yōu)化策略富弦。具體的做法就是將要查詢的數(shù)據(jù)作為索引列建立普通索引(可以是單列索引沟娱,也可以一個索引語句定義所有要查詢的列,即聯(lián)合索引)腕柜,這樣的話就可以直接返回索引中的的數(shù)據(jù)济似,不需要再通過聚集索引去定位行記錄,避免了回表的情況發(fā)生盏缤。
覆蓋索引的定義與注意事項
如果一個索引覆蓋(包含)了所有需要查詢的字段的值砰蠢,這個索引就是覆蓋索引。因為索引中已經(jīng)包含了要查詢的字段的值唉铜,因此查詢的時候直接返回索引中的字段值就可以了台舱,不需要再到表中查詢,避免了對主鍵索引的二次查詢潭流,也就提高了查詢的效率竞惋。
要注意的是,不是所有類型的索引都可以成為覆蓋索引的灰嫉。因為覆蓋索引必須要存儲索引的列值拆宛,而哈希索引、空間索引和全文索引等都不存儲索引列值熬甫,索引MySQL只能使用B-Tree索引做覆蓋索引胰挑。
另外,當(dāng)發(fā)起一個被索引覆蓋的查詢(索引覆蓋查詢)時椿肩,在explain(執(zhí)行計劃)的Extra列可以看到【Using Index】的信息瞻颂。
覆蓋索引的優(yōu)點
1.索引條目通常遠(yuǎn)小于數(shù)據(jù)行的大小,因為覆蓋索引只需要讀取索引郑象,極大地減少了數(shù)據(jù)的訪問量贡这。
2.索引是按照列值順序存儲的,對于IO密集的范圍查找會比隨機從磁盤讀取每一行數(shù)據(jù)的IO小很多厂榛。
3.一些存儲引擎比如MyISAM在內(nèi)存中只緩存索引盖矫,數(shù)據(jù)則依賴操作系統(tǒng)來緩存,因此要訪問數(shù)據(jù)的話需要一次系統(tǒng)調(diào)用击奶,使用覆蓋索引則避免了這一點辈双。
4.由于InnoDB的聚簇索引,覆蓋索引對InnoDB引擎下的數(shù)據(jù)庫表特別有用柜砾。因為InnoDB的二級索引在葉子節(jié)點中保存了行的主鍵值湃望,如果二級索引能夠覆蓋查詢,就避免了對主鍵索引的二次查詢痰驱。