今天這篇主要是針對索引,開篇前先對Mysql數(shù)據(jù)庫的性能有個整體的認(rèn)識,一般來講8c16g的數(shù)據(jù)庫qps在1000~2000,而16c32g的數(shù)據(jù)庫 qps在2000~4000左右,我們講qps也一般是針對單個系統(tǒng)、服務(wù)的性能衡量,而tps:一個業(yè)務(wù)系統(tǒng)(請求會調(diào)用多個其他服務(wù))的性能衡量。
索引篇
索引為什么要放在磁盤中
上一篇數(shù)據(jù)更新流程中,有講Innodb引擎會在內(nèi)存引入buffer pool,但是由于內(nèi)存容易丟失,一般而言會配合各種日志和刷盤策略,將數(shù)據(jù)持久化在磁盤文件中,但是和內(nèi)存相比,從磁盤中讀取數(shù)據(jù)的速度會慢上百倍千倍甚至萬倍,所以,我們應(yīng)當(dāng)盡量減少從磁盤中讀取數(shù)據(jù)的次數(shù)。另外,從磁盤中讀取數(shù)據(jù)時,都是按照磁盤塊來讀取的,并不是一條一條的讀。如果我們能把盡量多的數(shù)據(jù)放進(jìn)磁盤塊中,那一次磁盤讀取操作就會讀取更多數(shù)據(jù),那我們查找數(shù)據(jù)的時間也會大幅度降低。如果采用樹這種數(shù)據(jù)結(jié)構(gòu)作為索引的數(shù)據(jù)結(jié)構(gòu),那每查找一次數(shù)據(jù)就需要從磁盤中讀取一個節(jié)點(diǎn),也就是我們說的一個磁盤塊。
為什么不用平衡二叉樹(B-Tree)作為索引呢?
平衡二叉樹每個節(jié)點(diǎn)只存儲一個鍵值和數(shù)據(jù)的,這就意味著每個磁盤塊僅僅存儲一個鍵值和數(shù)據(jù)!存的越多,二叉樹的節(jié)點(diǎn)也會越多,并且高度也會極其高,查找數(shù)據(jù)時也會進(jìn)行很多次磁盤 IO,效率也非常低下!為了解決這個問題,就可以采用 B 樹結(jié)構(gòu)來解決層級過高。
B樹的每個節(jié)點(diǎn)稱為頁,頁就是前面提到的磁盤塊,B 樹相對于平衡二叉樹,每個節(jié)點(diǎn)存儲了更多的鍵值(key)和數(shù)據(jù)(data),并且每個節(jié)點(diǎn)擁有更多的子節(jié)點(diǎn),子節(jié)點(diǎn)的個數(shù)一般稱為階。 基于這個特性,B 樹查找數(shù)據(jù)讀取磁盤的次數(shù)將會很少,數(shù)據(jù)的查找效率也會比平衡二叉樹高很多。
為什么要用B+樹?
- B 樹節(jié)點(diǎn)中不僅存儲鍵值,也會存儲數(shù)據(jù),而B+ 樹非葉子節(jié)點(diǎn)上是不存儲數(shù)據(jù)的,僅存儲鍵值,如果不存儲數(shù)據(jù),那么就會存儲更多的鍵值,層高基本不會因?yàn)閿?shù)據(jù)擴(kuò)大而增高(三層樹結(jié)構(gòu)大概可以存放兩千萬數(shù)據(jù)量)。
- B+樹有利于磁盤的IO,數(shù)據(jù)越多,只需要提升樹的階數(shù)(節(jié)點(diǎn)的子節(jié)點(diǎn)樹)即可,樹就會更矮更胖,IO次數(shù)就更少。
- B+樹的所有數(shù)據(jù)都在葉子節(jié)點(diǎn)上,所以B+樹的查詢效率穩(wěn)定,一般都是查詢3次(根節(jié)點(diǎn)存放在內(nèi)存中)。B+ 樹索引的所有數(shù)據(jù)均存儲在葉子節(jié)點(diǎn),而且數(shù)據(jù)是按照順序排列的。范圍查找,排序查找,分組查找以及去重查找變得異常簡單,而B 樹因?yàn)閿?shù)據(jù)分散在各個節(jié)點(diǎn),要實(shí)現(xiàn)這一點(diǎn)是很不容易的。
- B+ 樹中各個頁之間是通過雙向鏈表連接的,葉子節(jié)點(diǎn)中的數(shù)據(jù)是通過單向鏈表連接的。在 InnoDB 中,我們通過數(shù)據(jù)頁之間通過雙向鏈表連接以及葉子節(jié)點(diǎn)中數(shù)據(jù)之間通過單向鏈表連接的方式可以找到表中所有的數(shù)據(jù)。
底層的索引結(jié)構(gòu)
MySQL的數(shù)據(jù)是以頁為基本單位組合而成的,頁的大小是16KB,里面包含我們的多條數(shù)據(jù),它還有指向下一頁的指針和指向上一頁的指針。Mysql取頁還有一個預(yù)讀機(jī)制(數(shù)據(jù)庫在查詢到一條數(shù)據(jù)的時候會把頁中相鄰的數(shù)據(jù)也取出來)。
將一個節(jié)點(diǎn)的大小設(shè)為等于一個頁,這樣每個節(jié)點(diǎn)只需要一次I/O就可以完全載入。假設(shè)B+Tree的高度為h,一次檢索最多需要h-1次I/O(根節(jié)點(diǎn)常駐內(nèi)存),復(fù)雜度O(h) = O(logmN)。實(shí)際應(yīng)用場景中,M通常較大,常常超過100,因此樹的高度一般都比較小,通常不超過3。
Mysql底層的B+樹結(jié)構(gòu)
Mysql會采用頁目錄的目錄項(xiàng)來指向一行數(shù)據(jù),這條數(shù)據(jù)就是存在于這個目錄項(xiàng)中的最小數(shù)據(jù),那么就可以通過頁目錄來查找所需數(shù)據(jù)。非葉子節(jié)點(diǎn)是雙向鏈表,葉子節(jié)點(diǎn)是單向鏈表。
每個節(jié)點(diǎn)就可以理解為是一個頁,而葉子節(jié)點(diǎn)也就是數(shù)據(jù)頁,除了葉子節(jié)點(diǎn)以外的節(jié)點(diǎn)就是目錄頁。非葉子節(jié)點(diǎn)只存放了索引,而只有葉子節(jié)點(diǎn)中存放了真實(shí)的數(shù)據(jù)。