InnoDB存儲引擎的關鍵特性包括插入緩衝、兩次寫(double write)、自適應哈希索引(adaptive hash index)。這些特性爲InnoDB存儲引擎帶來了更好的性能和更高的可靠性。html
插入緩衝是InnoDB存儲引擎關鍵特性中最使人激動的。不過,這個名字可能會讓人認爲插入緩衝是緩衝池中的一個部分。其實否則,InnoDB緩衝池中有Insert Buffer信息當然不錯,可是Insert Buffer和數據頁同樣,也是物理頁的一個組成部分。mysql
主鍵是行惟一的標識符,在應用程序中行記錄的插入順序是按照主鍵遞增的順序進行插入的。所以,插入彙集索引通常是順序的,不須要磁盤的隨機讀取。sql
好比說咱們按下列SQL定義的表:create table t(id int auto_increment,name varchar(30),primary key(id));數據庫
id列是自增加的,這意味着當執行插入操做時,id列會自動增加,頁中的行記錄按id執行順序存放。通常狀況下,不須要隨機讀取另外一頁執行記錄的存放。所以,在這樣的狀況下,插入操做通常很快就能完成。可是,不可能每張表上只有一個彙集索引,在更多的狀況下,一張表上有多個非彙集的輔助索引(secondary index)。好比,咱們還須要按照name這個字段進行查找,而且name這個字段不是惟一的。服務器
表是按以下的SQL語句定義的:create table t (id int auto_increment,name varchar(30),primary key(id),key(name));架構
這樣的狀況下產生了一個非彙集的而且不是惟一的索引。在進行插入操做時,數據頁的存放仍是按主鍵id的執行順序存放,可是對於非彙集索引,葉子節點的插入再也不是順序的了。這時就須要離散地訪問非彙集索引頁,插入性能在這裏變低了。然而這並非這個name字段上索引的錯誤,由於B+樹的特性決定了非彙集索引插入的離散性。app
InnoDB存儲引擎開創性地設計了插入緩衝,對於非彙集索引的插入或更新操做,不是每一次直接插入索引頁中,而是先判斷插入的非彙集索引頁是否在緩衝池中。若是在,則直接插入;若是不在,則先放入一個插入緩衝區中,好似欺騙數據庫這個非彙集的索引已經插到葉子節點了,而後再以必定的頻率執行插入緩衝和非彙集索引頁子節點的合併操做,這時一般能將多個插入合併到一個操做中(由於在一個索引頁中),這就大大提升了對非彙集索引執行插入和修改操做的性能。函數
插入緩衝的使用須要知足如下兩個條件:性能
當知足以上兩個條件時,InnoDB存儲引擎會使用插入緩衝,這樣就能提升性能了。不過考慮一種狀況,應用程序執行大量的插入和更新操做,這些操做都涉及了不惟一的非彙集索引,若是在這個過程當中數據庫發生了宕機,這時候會有大量的插入緩衝並無合併到實際的非彙集索引中。若是是這樣,恢復可能須要很長的時間,極端狀況下甚至須要幾個小時來執行合併恢復操做。優化
輔助索引不能是惟一的,由於在把它插入到插入緩衝時,咱們並不去查找索引頁的狀況。若是去查找確定又會出現離散讀的狀況,插入緩衝就失去了意義。
查看插入緩衝的信息:
show engine innodb status\G
seg size顯示了當前插入緩衝的大小爲2*16KB,free list len表明了空閒列表的長度,size表明了已經合併記錄頁的數量。
下面一行多是咱們真正關心的,由於它顯示了提升性能了。inserts表明插入的記錄數,merged recs表明合併的頁的數量,merges表明合併的次數。
merged recs:merges大約爲3:1,表明插入緩衝將對於非彙集索引頁的IO請求大約下降了3倍。
問題:
目前插入緩衝存在一個問題是,在寫密集的狀況下,插入緩衝會佔用過多的緩衝池內存,默認狀況下最大能夠佔用1/2的緩衝池內存。Percona已發佈一些patch來修正插入緩衝佔用太多緩衝池內存的問題,具體的能夠到http://www.percona.com/percona-lab.html查找。簡單來講,修改IBUF_POOL_SIZE_PER_MAX_SIZE就能夠對插入緩衝的大小進行控制,例如,將IBUF_POOL_SIZE_PER_MAX_SIZE改成3,則最大隻能使用1/3的緩衝池內存。
若是說插入緩衝帶給InnoDB存儲引擎的是性能,那麼兩次寫帶給InnoDB存儲引擎的是數據的可靠性。當數據庫宕機時,可能發生數據庫正在寫一個頁面,而這個頁只寫了一部分(好比16K的頁,只寫前4K的頁)的狀況,咱們稱之爲部分寫失效(partial page write)。在InnoDB存儲引擎未使用double write技術前,曾出現過由於部分寫失效而致使數據丟失的狀況。
有人也許會想,若是發生寫失效,能夠經過重作日誌進行恢復。這是一個辦法。可是必須清楚的是,重作日誌中記錄的是對頁的物理操做,如偏移量800,寫'aaaa'記錄。若是這個頁自己已經損壞,再對其進行重作是沒有意義的。這就是說,在應用(apply)重作日誌前,咱們須要一個頁的副本,當寫入失效發生時,先經過頁的副原本還原該頁,再進行重作,這就是doublewrite。
InnoDB存儲引擎doublewrite的體系架構如圖2-4所示:
doublewrite由兩部分組成:一部分是內存中的doublewrite buffer,大小爲2MB;另外一部分是物理磁盤上共享表空間中連續的128個頁,即兩個區(extent),大小一樣爲2MB(頁的副本)。當緩衝池的髒頁刷新時,並不直接寫磁盤,而是會經過memcpy函數將髒頁先拷貝到內存中的doublewrite buffer,以後經過doublewrite buffer再分兩次,每次寫入1MB到共享表空間的物理磁盤上,而後立刻調用fsync函數,同步磁盤,避免緩衝寫帶來的問題。在這個過程當中,由於doublewrite頁是連續的,所以這個過程是順序寫的,開銷並非很大。在完成doublewrite頁的寫入後,再將doublewrite buffer中的頁寫入各個表空間文件中,此時的寫入則是離散的。
能夠經過如下命令觀察到doublewrite運行的狀況: show global status like 'innodb_dblwr%'\G
doublewrite一共寫了18 445個頁,但實際的寫入次數爲434,(42:1) 基本上符合64:1。
若是發現你的系統在高峯時Innodb_dblwr_pages_written:Innodb_dblwr_writes遠小於64:1,那麼說明你的系統寫入壓力並非很高。
若是操做系統在將頁寫入磁盤的過程當中崩潰了,在恢復過程當中,InnoDB存儲引擎能夠從共享表空間中的doublewrite中找到改頁的一個副本,將其拷貝到表空間文件,再應用重作日誌。下面顯示了由doublewrite進行恢復的一種狀況:
090924 11:36:32 mysqld restarted 090924 11:36:33 InnoDB:Database was not shut down normally! InnoDB:Starting crash recovery. InnoDB:Reading tablespace information from the.ibd files…… InnoDB:Error:space id in fsp header 0,but in the page header 4294967295 InnoDB:Error:tablespace id 4294967295 in file./test/t.ibd is not sensible InnoDB:Error:tablespace id 0 in file./test/t2.ibd is not sensible 090924 11:36:33 InnoDB:Operating system error number 40 in a file operation. InnoDB:Error number 40 means'Too many levels of symbolic links'. InnoDB:Some operating system error numbers are described at InnoDB:http://dev.mysql.com/doc/refman/5.0/en/operating-system-error-codes.html InnoDB:File name./now/member InnoDB:File operation call:'stat'. InnoDB:Error:os_file_readdir_next_file()returned-1 in InnoDB:directory./now InnoDB:Crash recovery may have failed for some.ibd files! InnoDB:Restoring possible half-written data pages from the doublewrite InnoDB:buffer……
參數skip_innodb_doublewrite能夠禁止使用兩次寫功能,這時可能會發生前面說起的寫失效問題。不過,若是你有多臺從服務器(slave server),須要提供較快的性能(如slave上作的是RAID0),也許啓用這個參數是一個辦法。不過,在須要提供數據高可靠性的主服務器(master server)上,任什麼時候候咱們都應確保開啓兩次寫功能。
注意:有些文件系統自己就提供了部分寫失效的防範機制,如ZFS文件系統。在這種狀況下,咱們就不要啓用doublewrite了。
哈希(hash)是一種很是快的查找方法,通常狀況下查找的時間複雜度爲O(1)。經常使用於鏈接(join)操做,如SQL Server和Oracle中的哈希鏈接(hash join)。可是SQL Server和Oracle等常見的數據庫並不支持哈希索引(hash index)。MySQL的Heap存儲引擎默認的索引類型爲哈希,而InnoDB存儲引擎提出了另外一種實現方法,自適應哈希索引(adaptive hash index)。
InnoDB存儲引擎會監控對錶上索引的查找,若是觀察到創建哈希索引能夠帶來速度的提高,則創建哈希索引,因此稱之爲自適應(adaptive)的。自適應哈希索引經過緩衝池的B+樹構造而來,所以創建的速度很快。並且不須要將整個表都建哈希索引,InnoDB存儲引擎會自動根據訪問的頻率和模式來爲某些頁創建哈希索引。
根據InnoDB的官方文檔顯示,啓用自適應哈希索引後,讀取和寫入速度能夠提升2倍;對於輔助索引的鏈接操做,性能能夠提升5倍。自適應哈希索引是很是好的優化模式,其設計思想是數據庫自優化(self-tuning),即無需DBA對數據庫進行調整。
查看當前自適應哈希索引的使用情況:show engine innodb status\G
如今能夠看到自適應哈希索引的使用信息了,包括自適應哈希索引的大小、使用狀況、每秒使用自適應哈希索引搜索的狀況。值得注意的是,哈希索引只能用來搜索等值的查詢,如select * from table where index_col='xxx',而對於其餘查找類型,如範圍查找,是不能使用的。所以,這裏出現了non-hash searches/s的狀況。用hash searches:non-hash searches命令能夠大概瞭解使用哈希索引後的效率。
因爲自適應哈希索引是由InnoDB存儲引擎控制的,因此這裏的信息只供咱們參考。不過咱們能夠經過參數innodb_adaptive_hash_index來禁用或啓動此特性,默認爲開啓。