MySQL 高級 —— 深入理解 InnoDB 與 MyISAM

引言

InnoDB作爲事務型數據的首選存儲引擎,是中高級程序員必須掌握的知識,與之經常一同提起的MyISAM,也是在應用場景中頻繁會接觸的典型存儲引擎。

在《高性能MySQL》第五章中,有關於這兩種引擎的索引描述,本篇博客將結合書中內容進行總結和概括,幫助更好地理解其內部的存儲方式。

一、查看數據庫存儲引擎的SQL語句

SHOW ENGINES;

SHOW VARIABLES LIKE '%storage_engine%';

 

二、InnoDB 和 MyISAM 存儲引擎的比較

關於InnoDB和MyISAM的常規比較,下表是重點。

三、InnoDB 和 MyISAM 的數據分佈

在《高性能MySQL》第五章,作者圍繞着數據在兩種截然不同的存儲引擎中是如何存儲的進行了細緻的分析。

先來說說MyISAM存儲引擎。它對錶中數據有單獨的存儲文件,所謂“單獨的” 指的是數據和主鍵是分開存儲的。這一點與InnoDB有着本質的區別。這在數據庫領域,叫做——非聚簇索引

我思考了一下,如果讓我去設計一個存儲引擎,根據我的知識水平,多半就是會設計成MyISAM這樣的數據存儲結構。我們先來看一下它是如何來存儲數據和主鍵的:

首先,不論在InnoDB還是在MyISAM中,索引都是以B樹的形式來存儲的,這沒什麼好說的(參考《MySQL 高級 —— 索引實現的思考》),然後我們看到,主鍵索引樹中的葉子節點都會指向具體的數據行。

也就是說,MyISAM分開存儲了主鍵列和數據行,然後通過在主鍵索引的葉子節點中同時保存列值(主鍵值)和指向數據行的指針,從而實現關聯。這在計算機領域是一種非常典型的鍵值關聯的方式。這也是爲什麼我說,如果要我來設計存儲引擎,可能多半也是這樣做的原因,可以說MyISAM的數據存儲方式是非常簡單的。

MyISAM的二級索引的葉子節點同樣保存了指向數據行的指針。因此本質上,MyISAM的主鍵索引和普通的二級索引(或者叫輔助索引)沒有太大的區別。從上圖中也可以看出。

什麼是二級索引?

二級索引也叫輔助索引,是除主鍵索引以外的其他類型的索引。

InnoDB存儲引擎,相對於MyISAM就要複雜許多。

首先,它以聚簇索引的形式來組織數據,其次作爲聚簇索引的主鍵索引與二級索引也是有許多不同點:

InnoDB的聚簇索引就是主鍵索引,其葉子節點包含:主鍵的列值、事務ID、回滾指針、以及所有數據列

可以說,InnoDB整個表的邏輯結構就是通過主鍵的聚簇索引方式來存儲的,在InnoDB中,聚簇索引就是表

所謂“聚簇”,意思就是數據與主鍵存儲在一起。

另外,如果InnoDB的主鍵是一個列前綴索引,InnoDB還是會包含完整的主鍵列和剩下的其他列。這裏的列前綴,我的理解是主鍵列並不是完整的作爲索引列,而是“前綴”作爲索引列。比如,主鍵列值是123456,那麼這裏的列前綴可以是123,即僅取主鍵列的前綴作爲索引。

InnoDB的二級索引與MyISAM的二級索引有所不同,它不是類似於MyISAM那樣在葉子節點中保存“行指針”,而是保存主鍵值,以此來作爲“指針”。這是因爲當出現行移動或數據頁分裂時,可以避免對二級索引的維護操作。但這樣的代價可能是會讓二級索引佔用更多的空間。

對於非葉子節點,它包含了索引列和一個指向下級節點的指針,這對所有的 B樹索引都適用。

四、InnoDB爲什麼更推薦順序遞增id?

InnoDB更推薦使用自增id作爲聚簇索引的主鍵。

我們知道,B樹索引是按照索引列遞增的順序進行存儲的,InnoDB的主鍵索引也不例外。

在向InnoDB插入數據時,自增的 id 可以更快速地直接在數據末尾追加。MySQL數據的存儲以頁爲單位,當頁被插滿(達到頁的最大填充因子,默認15/16),下一條記錄就會寫入新的頁

而如果使用隨機值,如UUID作爲主鍵,因爲新行的UUID不一定比之前插入的記錄大,所以InnoDB無法簡單的把新行插入到索引的最後,而是需要爲新行尋找合適的位置通常是已有數據的中間位置。那麼之前已經寫滿的,並且已經刷到磁盤上的頁可能會被重新讀取。這會增加很多額外工作,並會導致數據分佈不夠優化。

隨機主鍵的缺點如下
1、寫入的目標頁可能已經刷到磁盤上,並且從緩存中移除,或者還沒有被加載到緩存中,就必須要先從磁盤中讀取目標頁,導致大量的隨機IO

2、因爲寫入是隨機的,InnoDB不得不頻繁的做頁分裂操作,以便爲新的行分配空間。頁分裂會移動大量的數據,一次插入最少需要修改三個頁面而不是一個。

3、由於頻繁的頁分裂,頁會變得稀疏並被不規則地填充,所以最終數據會有碎片。因此可能還需要做一次OPTIMIZE TABLE 來重建表並優化頁的填充。

什麼是 OPTIMIZE TABLE? 

語法:OPTIMIZE [LOCAL | NO_WRITE_TO_BINLOG] TABLE tbl_name

簡單的說,由於大量修改數據,如刪除、移動等,造成的存儲空間利用不均,導致的數據碎片。那麼就可以使用OPTIMIZE TABLE 來優化數據表,從而更好的利用未使用的空間,整理數據文件的碎片

一般情況下,根本不需要運行 OPTIMIZE TABLE,即使對可變長度的行進行了大量的更新,也不需要頻繁運行,每週一次或每月一次即可。只對MyISAM、BDB、InnoDB表有效。OPTIMIZE TABLE時,MySQL會鎖表。

另外,順序主鍵也不一定是完全無害的,在高併發場景,順序插入可能會造成明顯的爭用,主鍵的上界會成爲"熱點",這可能會使併發插入導致間隙鎖競爭。

還有另一個熱點可能是AUTO_INCREMENT鎖機制。這些問題可能需要重新設計表或應用,或者更改 innodb_autoinc_lock_mode設置。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章