MySQL 高级 —— 深入理解 InnoDB 与 MyISAM
引言
在文件系統中,MySQL將每個數據庫(也可以稱之為schema)保存為數據目錄下的一個子目錄。創建表時,MySQL會在數據庫子目錄下創建一個與表同名的.frm文件保存表的定義。因為MySQL使用文件系統的目錄和文件來保存數據庫和表的定義,在Windows中,大小寫不是敏感的;而在Linux系統中,則大小寫敏感。不同的存儲引擎保存數據和索引的方式是不同的,但表的定義則是在MySQL服務器層統一處理的。
InnoDB作為事務型數據的首選存儲引擎,是中高級程序員必須掌握的知識,與之經常一同提起的MyISAM,也是在應用場景中頻繁會接觸的典型存儲引擎。
在《高性能MySQL》第五章中,有關于這兩種引擎的索引描述,本篇博客將結合書中內容進行總結和概括,幫助更好地理解其內部的存儲方式。
一、查看數據庫存儲引擎的SQL語句
SHOW ENGINES; SHOW VARIABLES LIKE '%storage_engine%';?
另外,還可以通過SHOW TABLE?STATUS來查看表的狀態信息,里面會包含與表相關的動態信息展示:
SHOW TABLE STATUS LIKE 'teacher'; Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment ------- ------ ------- ---------- ------ -------------- ----------- --------------- ------------ --------- -------------- ------------------- ----------- ---------- --------------- -------- -------------- --------- teacher InnoDB 10 Dynamic 18 910 16384 0 81920 0 19 2020-05-24 16:13:32 (NULL) (NULL) utf8_general_ci (NULL)Row_format 可選三個值:Dynamic、Fixed或者Compressed。Dynamic表示行長度是可變的,一般包含可變長度的字段,如VARCHAR或 BLOB。Fixed表示行長度固定,只包含固定長度的列,如CHAR和INTEGER。Compressed只在壓縮表中存在。
Rows:表中行數,MyISAM是一個精確值,InnoDB是估計值。
Avg_row_length:平均每行包含的字節數。
Data_length:表數據的大小。
Max_data_length:表數據的最大容量,該值與存儲引擎有關。
Index_length:索引的字節數。
Data_free:對于MyISAM表,表示已分配但目前沒有使用的空間。這部分空間包括了之前刪除的行,以及后續可以被INSERT 利用到的空間。
Auto_increment:下一個AUTO_INCREMENT的值。
二、InnoDB 和 MyISAM 存儲引擎的比較
關于InnoDB和MyISAM的常規比較,下表是重點:
2.1 InnoDB
InnoDB是MySQL默認的事務型引擎,也是最重要、使用最廣泛的存儲引擎,被設計成用來處理大量短期事務,短期事務大部分情況是正常提交的,很少會被回滾。
InnoDB采用MVCC(多版本并發控制)來支持高并發,并且實現了四個標準的隔離級別。其默認級別是可重復讀,并且通過間隙鎖(next-key locking)策略防止幻讀的出現。
間隙鎖使得InnoDB不僅僅鎖定查詢涉及的行,還會對索引中的間隙進行鎖定,以防止幻影行的插入。
InnoDB表是基于聚簇索引建立的。InnoDB的索引結構和MySQL的其他存儲引擎有很大不同,聚簇索引對主鍵查詢有很高的性能。不過它的二級索引(secondary index,非主鍵索引)中必須包含主鍵列,所以如果主鍵列很大的話,其他的所有索引都會很大。因此,如果表上的索引較多的話,主鍵應當盡可能的小。
InnoDB的存儲格式是平臺無關的,因此可以將數據和索引文件在不同的平臺上復制遷移。
InnoDB內部做了很多優化,包括從磁盤讀取數據時采用的可預測性預讀,能夠自動在內存中創建hash索引以加速讀操作的自適應哈希索引(adaptive hash index),以及能夠加速插入操作的插入緩沖區(insert buffer)等。
2.2 MyISAM
在MySQL5.1 及之前的版本,MyISAM是默認的存儲引擎。
MyISAM提供了大量的特性:全文索引、壓縮、空間函數等。但MyISAM不支持事務和行級鎖,而且崩潰后無法安全恢復。但對于只讀的數據,或表比較小、可以忍受修復操作,依然可以選擇MyISAM。
MyISAM會將表存儲在兩個文件中:數據文件和索引文件。分別以.MYD和.MYI為擴展名。
MyISAM表可以包含動態或靜態(固定長度)行。MyISAM表可以存儲的行記錄數,一般受限于可用的磁盤空間,或者操作系統單個文件的最大尺寸。
作為MySQL最早的存儲引擎之一,MyISAM有一些已經開發出來很多年的特性:
①加鎖和并發:MyISAM對整張表加鎖,而不是針對行。讀取時會對需要讀到的所有表加共享鎖,寫入時則對表加排他鎖。但在表有讀取查詢的同時,也可以往表里插入新的記錄,這被稱為并發插入——CONCURRENT INSERT。
②修復:MyISAM可以手工或自動執行檢查和修復操作,但這里的修復并不是事務恢復或崩潰恢復。執行表的修復可能導致一些數據丟失,而且修復操作是非常慢的。可以通過CHECK TABLE mytable檢查表的錯誤,如果有錯誤,可以通過執行REPAIR TABLE mytable進行修復。
③索引特性:對于MyISAM,即使BLOB和TEXT等長字段,也可以基于前500個字符創建索引。MyISAM也支持全文索引,這是一種基于分詞創建的索引,可以支持復雜的查詢。
④延遲更新索引鍵(Delayed Key Write):創建MyISAM表的時候,如果指定了DELAY_KEY_WRITE選項,在每次修改執行完成時,不會立刻將修改的索引數據寫入磁盤,而是會寫到內存中的緩沖區。只有在清理緩沖區或者關閉表的時候才會將對應的索引塊寫入到磁盤。這種方式極大的提升了寫入性能,但在數據庫或主機崩潰時會造成索引損壞,需要執行修復操作。延遲更新索引的特性可以在全局設置,也可以在單個表設置。
如果表在創建并導入數據之后,不會再進行修改操作,那么這樣的表或許適合采用MyISAM壓縮表。
可以使用myisampack對MyISAM表進行壓縮(也叫打包pack)。壓縮表中的數據是不可以直接修改的,但可以先解壓縮、修改數據、再壓縮。
壓縮表可以極大的減少磁盤空間占用,因此也可以減少磁盤IO,從而提升查詢性能。壓縮表也支持索引,但索引也是只讀的。
以目前的硬件能力,大多數場景下,讀取壓縮表數據時的解壓開銷影響并不大,而減少IO帶來的好處是非常明顯的。壓縮表中的記錄是獨立記錄的,所以讀取單行的時候不需要解壓整張表,甚至不需要解壓行所在的頁面。
MyISAM引擎設計簡單,數據以緊密格式存儲,所以在某些場景下的性能很好。但MyISAM最典型的性能問題是表鎖的問題,如果你發現所有的查詢都長期處于“Locked”狀態,那么毫無疑問表鎖是罪魁禍首。
三、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設置。
五、選擇合適的存儲引擎
MySQL其實還有很多其他存儲引擎,這里建議如何選擇存儲引擎的原則是“除非需要用到某些InnoDB不具備的特性,并且沒有其他辦法可以替代,否則都應該優先選擇InnoDB”。
例如,如果要用到全文索引,建議優先考慮InnoDB加上Sphinx的組合,而不是使用支持全文索引的MyISAM。
如果應用需要不同的存儲引擎,請先考慮一下幾個因素:
①事務:如果需要事務,則使用InnoDB是最好的選擇,如果不需要事務,并且主要是SELECT和INSERT操作,那么MyISAM是不錯的選擇。一般日志型應用比較符合這一特性。
②備份:如果可以定期關閉服務器來執行備份,那么備份的因素可以忽略。如果需要在線熱備份,那么最好選擇InnoDB。
③崩潰恢復:很多人即使不需要事務支持,也會選擇InnoDB的原因,就是它可以在系統崩潰后快速地恢復數據。
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的MySQL 高级 —— 深入理解 InnoDB 与 MyISAM的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python selenium ifra
- 下一篇: linux cmake编译源码,linu