一、mysql的Innodb引擎中,主鍵索引和普通索引的工作原理
在InnoDB中,表都是根據(jù)主鍵順序以索引的形式存放的,這種存儲(chǔ)方式的表稱(chēng)為索引組織表。InnoDB使用了B+樹(shù)索引模型,所以數(shù)據(jù)都是存儲(chǔ)在B+樹(shù)中的。
每一個(gè)索引在InnoDB里面對(duì)應(yīng)一棵B+樹(shù)。
假設(shè),我們有一個(gè)主鍵列為ID的表,表中有字段k,并且在k上有索引。
這個(gè)表的建表語(yǔ)句是:
mysql> create table T(
id int primary key,
k int not null,
name varchar(16),
index (k))engine=InnoDB;
主鍵索引的葉子節(jié)點(diǎn)存的是整行數(shù)據(jù)。在InnoDB里,主鍵索引也被稱(chēng)為聚簇索引(clustered index)。
非主鍵索引的葉子節(jié)點(diǎn)內(nèi)容是主鍵的值。在InnoDB里,非主鍵索引也被稱(chēng)為二級(jí)索引(secondary index)或普通索引。
根據(jù)上面的索引結(jié)構(gòu)說(shuō)明,我們來(lái)討論一個(gè)問(wèn)題:基于主鍵索引和普通索引的查詢(xún)有什么區(qū)別?
如果語(yǔ)句是select * from T where ID=500,即主鍵查詢(xún)方式,則只需要搜索ID這棵B+樹(shù);如果語(yǔ)句是select * from T where k=5,即普通索引查詢(xún)方式,則需要先搜索k索引樹(shù),得到ID的值為500,再到ID索引樹(shù)搜索一次。這個(gè)過(guò)程稱(chēng)為回表。 也就是說(shuō),基于非主鍵索引的查詢(xún)需要多掃描一棵索引樹(shù)。這也是為什么說(shuō)我們要盡量使用主鍵查詢(xún)了。延伸閱讀:
二、索引維護(hù)
B+樹(shù)為了維護(hù)索引有序性,在插入新值的時(shí)候需要做必要的維護(hù)。以上面這個(gè)圖為例,如果插入新的行ID值為700,則只需要在R5的記錄后面插入一個(gè)新記錄。如果新插入的ID值為400,就相對(duì)麻煩了,需要邏輯上挪動(dòng)后面的數(shù)據(jù),空出位置。
而更糟的情況是,如果R5所在的數(shù)據(jù)頁(yè)已經(jīng)滿(mǎn)了,根據(jù)B+樹(shù)的算法,這時(shí)候需要申請(qǐng)一個(gè)新的數(shù)據(jù)頁(yè),然后挪動(dòng)部分?jǐn)?shù)據(jù)過(guò)去。這個(gè)過(guò)程稱(chēng)為頁(yè)分裂。在這種情況下,性能自然會(huì)受影響。
除了性能外,頁(yè)分裂操作還影響數(shù)據(jù)頁(yè)的利用率。原本放在一個(gè)頁(yè)的數(shù)據(jù),現(xiàn)在分到兩個(gè)頁(yè)中,整體空間利用率降低大約50%。
當(dāng)然有分裂就有合并。當(dāng)相鄰兩個(gè)頁(yè)由于刪除了數(shù)據(jù),利用率很低之后,會(huì)將數(shù)據(jù)頁(yè)做合并。合并的過(guò)程,可以認(rèn)為是分裂過(guò)程的逆過(guò)程。