推薦閱讀:
存儲引擎
不少文章都是直接開始介紹有哪些存儲引擎,並無去介紹存儲引擎自己。那麼究竟什麼是存儲引擎?不知道你們有沒有想過,MySQL是如何存儲咱們丟進去的數據的?緩存
其實存儲引擎也很簡單,我認爲就是一種存儲解決方案,實現了新增數據、更新數據和創建索引等等功能。安全
有哪些已有的存儲引擎可讓咱們選擇呢?架構
InnoDB、MyISAM、Memory、CSV、Archive、Blackhole、Merge、Federated、Example性能
種類不少,可是經常使用的存儲引擎目前就只有InnoDB和MyISAM,我也會着重來介紹這兩種存儲引擎。學習
InnoDB是目前使用最廣的MySQL存儲引擎,MySQL從5.5版本開始InnoDB就已是默認的存儲引擎了。那你知道爲何InnoDB被普遍的使用呢?先把這個問題放一放,咱們先來了解一下InnoDB存儲引擎的底層原理。優化
InnoDB的內存架構主要分爲三大塊,緩衝池(Buffer Pool)、重作緩衝池(Redo Log Buffer)和額外內存池url
緩衝池
InnoDB爲了作數據的持久化,會將數據存儲到磁盤上。可是面對大量的請求時,CPU的處理速度和磁盤的IO速度之間差距太大,爲了提升總體的效率, InnoDB引入了緩衝池。
當有請求來查詢數據時,若是緩存池中沒有,就會去磁盤中查找,將匹配到的數據放入緩存池中。一樣的,若是有請求來修改數據,MySQL並不會直接去修改磁盤,而是會修改已經在緩衝池的頁中的數據,而後再將數據刷回磁盤,這就是緩衝池的做用,加速讀,加速寫,減小與磁盤的IO交互。
緩衝池說白了就是把磁盤中的數據丟到內存,那既然是內存就會存在沒有內存空間能夠分配的狀況。因此緩衝池採用了LRU算法,在緩衝池中沒有空閒的頁時,來進行頁的淘汰。可是採用這種算法會帶來一個問題叫作緩衝池污染。
當你在進行批量掃描甚至全表掃描時,可能會將緩衝池中的熱點頁所有替換出去。這樣一來可能會致使MySQL的性能斷崖式降低。因此InnoDB對LRU作了一些優化,規避了這個問題。
MySQL採用日誌先行,在真正寫數據以前,會首先記錄一個日誌,叫Redo Log,會按期的使用CheckPoint技術將新的Redo Log刷入磁盤,這個後面會講。
除了數據以外,裏面還存儲了索引頁、Undo頁、插入緩衝、自適應哈希索引、InnoDB鎖信息和數據字典。下面選幾個比較重要的來簡單聊一聊。
插入緩衝
插入緩衝針對的操做是更新或者插入,咱們考慮最壞的狀況,那就是須要更新的數據都不在緩衝池中。那麼此時會有下面兩種方案。
- 來一條數據就直接寫入磁盤
- 等數據達到某個閾值(例如50條)才批量的寫入磁盤
很明顯,第二種方案要好一點,減小了與磁盤IO的交互。
兩次寫
鑑於都聊到了插入緩衝,我就不得不須要提一嘴兩次寫,由於我認爲這兩個InnoDB的特性是相輔相成的。
插入緩衝提升了MySQL的性能,而兩次寫則在此基礎上提升了數據的可靠性。咱們知道,當數據還在緩衝池中的時候,當機器宕機了,發生了寫失效,有Redo Log來進行恢復。可是若是是在從緩衝池中將數據刷回磁盤的時候宕機了呢?
這種狀況叫作部分寫失效,此時重作日誌就沒法解決問題。
在刷網頁時,並非直接刷入磁盤,而是copy到內存中的Doublewrite Buffer中,而後再拷貝至磁盤共享表空間(你能夠就理解爲磁盤)中,每次寫入1M,等copy完成後,再將Doublewrite Buffer中的頁寫入磁盤文件。
有了兩次寫機制,即便在刷髒頁時宕機了,在實例恢復的時候也能夠從共享表空間中找到Doublewrite Buffer的頁副本,直接將其覆蓋原來的數據頁便可。
自適應哈希索引
自適應索引就跟JVM在運行過程當中,會動態的把某些熱點代碼編譯成Machine Code同樣,InnoDB會監控對全部索引的查詢,對熱點訪問的頁創建哈希索引,以此來提高訪問速度。
你可能屢次看到了一個關鍵字頁,接下來那咱們就來聊一下頁是什麼?
頁
頁,是InnoDB中數據管理的最小單位。當咱們查詢數據時,其是以頁爲單位,將磁盤中的數據加載到緩衝池中的。同理,更新數據也是以頁爲單位,將咱們對數據的修改刷回磁盤。每頁的默認大小爲16k,每頁中包含了若干行的數據,頁的結構以下圖所示。
不用太糾結每一個區是幹嗎的,咱們只須要知道這樣設計的好處在哪兒。每一頁的數據,能夠經過FileHeader中的上一頁和下一頁的數據,頁與頁之間能夠造成雙向鏈表。由於在實際的物理存儲上,數據並非連續存儲的。你能夠把它理解成G1的Region在內存中的分佈。
而一頁中所包含的行數據,行與行之間則造成了單向鏈表。咱們存入的行數據最終會到User Records中,固然最初User Records並不佔據任何存儲空間。隨着咱們存入的數據愈來愈多,User Records會愈來愈大,Free Space的空間會愈來愈小,直到被佔用完,就會申請新的數據頁。
User Records中的數據,是按照主鍵id來進行排序的,當咱們按照主鍵來進行查找時,會沿着這個單向鏈表一直日後找,
重作日誌緩衝
上面聊過,InnoDB中緩衝池中的頁數據更新會先於磁盤數據更新的,InnoDB也會採用日誌先行(Write Ahead Log)策略來刷新數據,什麼意思呢?當事務開始時,會先記錄Redo Log到Redo Log Buffer中,而後再更新緩衝池頁數據。
Redo Log Buffer中的數據會按照必定的頻率寫到重作日誌中去。被更改過的頁就會被標記成髒頁,InnoDB會根據CheckPoint機制來將髒頁刷到磁盤。
日誌
上面提到了Redo log,這一小節就專門來說一講日誌,日誌分爲以下兩個維度。
MySQL層面
InnoDB層面
MySQL日誌
MySQL的日誌能夠分爲錯誤日誌、二進制文件、查詢日誌和滿查詢日誌。
- 錯誤日誌 很好理解,就是服務運行過程當中發生的嚴重錯誤日誌。當咱們的數據庫沒法啓動時,就能夠來這裏看看具體不能啓動的緣由是什麼
- 二進制文件 它有另一個名字你應該熟悉,叫Binlog,其記錄了對數據庫全部的更改。
- 查詢日誌 記錄了來自客戶端的全部語句
- 慢查詢日誌 這裏記錄了全部響應時間超過閾值的SQL語句,這個閾值咱們能夠本身設置,參數爲
long_query_time
,其默認值爲10s,且默認是關閉的狀態,須要手動的打開。
InnoDB日誌
InnoDB日誌就只有兩種,Redo Log和Undo Log,
Redo Log 重作日誌,用於記錄事務操做的變化,且記錄的是修改以後的值。無論事務是否提交都會記錄下來。例如在更新數據時,會先將更新的記錄寫到Redo Log中,再更新緩存中頁中的數據。而後按照設置的更新策略,將內存中的數據刷回磁盤。
Undo Log 記錄的是記錄的事務開始以前的一個版本,可用於事務失敗以後發生的回滾。
Redo Log記錄的是具體某個數據頁上的修改,只能在當前Server使用,而Binlog能夠理解爲能夠給其餘類型的存儲引擎使用。這也是Binlog的一個重要做用,那就是主從複製,另一個做用是數據恢復。
上面提到過,Binlog中記錄了全部對數據庫的修改,其記錄日誌有三種格式。分別是Statement、Row和MixedLevel。
- Statement 記錄全部會修改數據的SQL,其只會記錄SQL,並不須要記錄下這個SQL影響的全部行,減小了日誌量,提升了性能。可是因爲只是記錄執行語句,不能保證在Slave節點上可以正確執行,因此還須要額外的記錄一些上下文信息
- Row 只保存被修改的記錄,與Statement只記錄執行SQL來比較,Row會產生大量的日誌。可是Row不用記錄上下文信息了,只須要關注被改爲啥樣就行。
- MixedLevel 就是Statement和Row混合使用。
具體使用哪一種日誌,須要根據實際狀況來決定。例如一條UPDATE語句更新了不少的數據,採用Statement會更加節省空間,可是相對的,Row會更加的可靠。
InnoDB和MyISAM的區別
因爲MyISAM並不經常使用,我也不打算去深究其底層的一些原理和實現。咱們在這裏簡單的對比一下這兩個存儲引擎的區別就好。咱們分點來一點點描述。
- 事務 InnoDB支持事務、回滾、事務安全和崩潰恢復。而MyISAM不支持,但查詢的速度要比InnoDB更快
- 主鍵 InnoDB規定,若是沒有設置主鍵,就自動的生成一個6字節的主鍵,而MyISAM容許沒有任何索引和主鍵的存在,索引就是行的地址
- 外鍵 InnoDB支持外鍵,而MyISAM不支持
- 表鎖 InnoDB支持行鎖和表鎖,而MyISAM只支持表鎖
- 全文索引 InnoDB不支持全文索引,可是能夠用插件來實現相應的功能,而MyISAM是自己就支持全本索引
- 行數 InnoDB獲取行數時,須要掃全表。而MyISAM保存了當前表的總行數,直接讀取便可。
因此,簡單總結一下,MyISAM只適用於查詢大於更新的場景,若是你的系統查詢的狀況佔絕大多數(例如報表系統)就可使用MyISAM來存儲,除此以外,都建議使用InnoDB。
End
因爲時間的緣由,本文只是簡單的聊了聊InnoDB的總體架構,並無很深刻的去聊某些點。例如InnoDB是如何改進來解決緩衝池污染的,其算法具體是什麼,checkpoint是如何工做的等等,只是作一個簡單的瞭解,以後若是有時間的話再細聊。
本文同步分享在 博客「Java入門到入墳」(JianShu)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。