InnoDB是一個平衡了高可靠性和高性能的通用存儲引擎.在MySQL的5.6版本中,InnoDB是默認的MySQL存儲引擎. 除非你配置了不一樣的默認存儲引擎,不然不帶ENGINE=字句的CREATE TABLE語句將建立一個InnoDB的表.html
像mysql和INFORMATION_SCHEMA這些實現了MySQL內部結構的數據庫,仍是使用的MyISAM引擎.而且不能將它們中的受權表切換爲InnoDB.
這裏只挑選了一些我的認爲對比較重要的點,更多細節請參考Benefits of Using InnoDB Tables.mysql
ACID模式是一系列強調對業務數據和關鍵任務應用程序很重要的可靠性方面的數據庫設計原則.算法
InnoDB是一個多版本存儲引擎(multi-versioned storage engine):它保存了被修改行的老版本信息來支持像併發性和rollback這樣的事務性功能.該信息被存儲在表空間中一個叫回滾段(rollback segment)的數據結構之中.InnoDB使用這些信息來支持事務回滾中的undo操做,也使用這些信息來爲一致性讀(consistent read)操做構建一行數據記錄的早期版本.sql
InnoDB會爲數據庫中的每一行數據添加3個字段:數據庫
DB_TRX_ID:6-byte大小,用來存儲上一個插入或者修改當前行的事務id.刪除也被當作一個特殊的修改,用一個bit位來標記當前數據行已被刪除.
DB_ROLL_PTR:7-byte大小,該指針指向一個回滾段中的undo日誌記錄.若是當前行被修改,那麼undo日誌中包含重建修改前數據的全部必要信息.
DB_ROW_ID:6-byte大小,存儲一個自增id值.若是InnoDB自動生成 聚簇索引(用戶沒有指定主鍵的狀況下),索引中就會包含該字段的值.
回滾段中的undo日誌分爲插入和修改.插入undo日誌只在事務回滾時使用,而且事務一旦提交就能夠丟棄.修改undo日誌不只用於事務回滾,也會用於一致性讀,只有當沒有事務須要使用修改undo日誌來爲一致性讀構建數據記錄早期版本的快照(snapshot)時才能進行丟棄.所以,要常常提交你的事務,包括那些只與讀操做相關的事務.不然,InnoDB不會丟棄修改undo日誌,這樣就會致使回滾段變得愈來愈大以致於填滿你的表空間(tablespace).一個回滾段中的undo日誌記錄的物理大小通常來講會比被插入或被修改的相應的數據行要小,你能夠據此來推算回滾段須要的空間大小.緩存
在InnoDB的多版本方案中,當你用SQL語句刪除一個數據行的時候,它並不會立刻從數據庫中被物理刪除.只有當爲刪除操做創建的修改undo日誌被丟棄以後InnoDB纔會物理的刪除相應的數據行和它的索引.這種移除操做被稱爲清除(purge),它很是快,一般採用和執行刪除操做的SQL語句相同的時間順序.安全
若是以恆定的速率在表中小批量的插入和刪除數據行,清除線程(purge thread)會由於來不及處理清除操做而開始滯後,表就會由於那些已經‘死亡’的數據行而變得愈來愈大,進而產生disk-bound而且會變得很慢.能夠經過調整innodb_max_purge_lag來避免上述狀況,可是應該根據具體狀況進行分析.bash
InnoDB的MVCC(multi-version concurrency control)對待二級索引(secondary index)和聚簇索引(clustered index)不太同樣.在聚簇索引中的數據是實時更新,而且他們包含隱藏的系統列.二級索引數據不包含隱藏的系統列也不是實時更新的.服務器
當一個二級索引列被修改時,老的二級索引數據被標記爲刪除,新的數據會被插入,最終被標記爲刪除的數據會被清除.當一個二級索引記錄被標記刪除或二級索引頁被一個新的事務修改時,InnoDB將會在聚簇索引中查詢數據庫記錄.在聚簇索引中會檢查記錄的DB_TRX_ID字段,以便在讀事務開始後若是記錄被其餘事務修改的狀況下返回正確的undo日誌.session
若是一個二級索引記錄被標記爲刪除或者二級索引頁被一個新的事務修改,覆蓋索引(covering index)技術將不會被使用.InnoDB會在聚簇索引中查找記錄,而不是直接從索引結構中返回數據.
若是啓用了index condition pushdown(ICP)優化,而且能夠僅使用索引中的字段來評估部分Where條件,則mysql服務器仍會將這部分Where條件下推到存儲引擎中,並使用索引對其進行評估.若是找不到匹配的記錄,那麼就避免了去聚簇索引中查找.若是找到了匹配的記錄,即便是被標記爲刪除的記錄,InnoDB都會到聚簇索引中查找記錄.
Buffer Pool是一個用於存放被訪問的表和索引數據的內存區域.Buffer Pool越大,mysql越像一個內存數據庫.
爲了提高大數據量讀操做的效率,buffer pool被劃分爲可能包含多行數據的頁(pages).爲了高效的管理緩存,buffer pool被實現爲一個linked list,鏈表中的每個節點就是一個page.Buffer pool使用一個LRU(least recently used)算法的變體來實現數據的管理.
Buffer Pool的LRU算法.
當須要空間來添加一個新的page時,最近最少使用的page將被丟棄而且新的page將被插入到鏈表的中間.這個中點插入算法(midpoint insertion strategy)將該list看成兩個子列表:
下圖是Mysql官網中的buffer pool鏈表的結構圖
這個算法使被頻繁訪問的pages處於new sublist中.Old sublist中存放不多使用的pages,這些pages是將被丟棄的候選pages.
默認狀況下,算法操做以下:
默認狀況下,經過query語句讀取的pages會立刻被移動到new sublist中,意味着他們能在buffer pool存活更長的時間.一個全表掃描(例如一次mysqldump操做,或者一個沒有where條件的select語句)會使大量的數據進入buffer pool中而且會丟棄相同數量的老數據,儘管這些新的數據將不會被再次使用.相似的還有經過預讀後臺線程加載的pages,在被訪問一次以後就會被放入new sublist的頭節點中,儘管以後可能並不會訪問這些pages.這些狀況將致使那些真正會被常常訪問的pages進入到old sublist,這樣他們就變成了可能被丟棄的對象.
Buffer pool配置
你能夠配置buffer pool的各個方面提升性能.
innodb_buffer_pool_size
來配置buffer pool的大小.Change buffer是一個特殊的數據結構用來緩存不在buffer pool中的二級索引頁的變動 .這些變動,可能來自INSERT,UPDATE或者DELETE操做,將在這些pages被其餘讀操做加載到buffer pool中以後被合併.
參考MySQL官網中的結構圖:
不像聚簇索引,二級索引一般並非惟一的,並且插入二級索引是以一個相對隨機的順序.相似的,刪除和修改操做可能會影響在索引樹種不相鄰的二級索引頁.當pages被其餘操做讀取到buffer pool中時纔去合併緩存的變動,這樣避免了大量的隨機I/O訪問將二級索引頁從disk中讀取到buffer pool中.
按期的,在系統空閒或者在slow shutdown期間運行的清洗操做(purge operation),會將被修改的索引頁寫入到磁盤中.清洗操做(purge operation)能夠將一系列索引數據寫入到磁盤塊中,這樣比將每個數據立馬寫入磁盤更加高效.
當有不少被影響的行和不少二級索引須要更新時,Change buffer合併可能要花費好幾個小時.在這期間,磁盤I/O可能會增長,這可能致使disk-bound的查詢顯著的變慢.Change buffer合併可能在一個事務被提交,甚至在一個服務被showdown或restart以後也會持續發生.
在內存中,change buffer佔據了一部分buffer pool.在磁盤上,change buffer是系統表空間的一部分,這是當服務shut down的時候來緩存索引變動的.
在change buffer中緩存的數據的類型是受 innodb_change_buffering
變量控制的.
當一個索引包含降序的索引列或主鍵包含一個降序的索引列時,二級索引不支持變動緩存.
配置Change Buffering
當INSERT、UPDATE和DELETE操做在一個表上被執行時,被索引列的值一般是未排序的,須要大量的I/O來使二級索引實時更新.當相關的page不在buffer pool中時,Change buffer會緩存對這些二級索引項的修改,這樣就避免了當即從磁盤讀取page的昂貴的I/O操做.這些被緩存的變動會在相關的page被加載到buffer pool中時被合併到page中,這些被修改的page會在隨後被刷新到磁盤上.InnoDB的主線程會在服務空閒或在slow shutdown期間對這些緩存變動進行合併.
由於它能減小磁盤的讀寫,所以change buffer對那些磁盤綁定的工做負載最優價值,例若有高頻DML操做(例如大量的insert)的應用.
Change buffer佔用了一部分的buffer pool,所以會減小緩存數據頁的內存空間.若是工做集大部分都能在buffer pool中(也就是說要操做的大部分pages都已經在buffer pool中),或者你的表只有相應的一些二級索引,那麼禁用change buffering多是有效的.由於change buffer只對那些不在buffer pool中的pages有用.
能夠經過 innodb_change_buffering
配置參數來控制change buffering的範圍.一個修改操做是一個插入操做和刪除操做的聯合.可用的 innodb_change_buffering
值以下:
你能夠經過在MySQL的配置文件(my.cnf或者my.ini)中的 innodb_change_buffering
設置或者經過 SET GLOBAL
語句來進行動態配置.
配置Change Buffer最大大小
innodb_change_buffer_max_size
參數容許配置change buffer佔buffer pool的最大百分比.默認是25,最大能設置爲50.
基於觀察搜索的模式,來利用索引key的前綴來建立hash索引.這個前綴能夠是任意長度的,可能只有一個B-tree的的值會出如今hash索引中.Hash索引是針對那些常常被訪問的索引頁來創建的.
若是一個表的數據基本均可以在內存中匹配,那麼hash索引能夠提升查詢的速度.InnoDB有一種機制監控索引查詢.若是InnoDB以爲查詢能夠受益於建立hash索引,那麼它就會自動創建hash索引.
在某些工做負載下,hash索引查詢的速度提高遠大於監控索引查詢和維護hash索引結構的額外工做.在過量工做負載的狀況下,訪問自適應hash索引有時可能會成爲競爭的一個源頭,例如多個併發鏈接(我理解當存在併發狀況的時候,只有一個線程會去建立hash索引,所以會存在競爭).LIKE語句不會使用hash索引.在MySQL 5.6版本使得禁用自適應hash索引比以前的版本更加合適.
Log buffer是存儲要那些將要寫入磁盤日誌文件中的數據的一塊內存區域.能夠經過innodb_log_buffer_size
來定義log buffer的大小.默認大小是16MB.Log buffer中的內容是週期性的刷新到磁盤中.一個大的log buffer可使那些大的事務在事務提交前不須要將redo log數據寫入到磁盤中.所以,若是你有會修改、插入或者刪除不少行的事務,那麼你能夠增長log buffer的大小來減小I/O操做.
innodb_flush_log_at_trx_commit
參數控制log buffer的數據是如何寫入和刷新到磁盤的.innodb_flush_log_at_timeout
參數控制log的刷新頻率.
過長的事務:當事務開啓時,系統會在事務開始時保存一份數據的快照,所以若是存在大量的插入、修改或刪除操做使,會致使大量的開銷.
太短的事務:太短的事務會致使頻繁的I/O操做,也會產生大量開銷.
處理死鎖(Deadlocks):對InnoDB表來講,deadlock並非一個嚴重的問題,一般也不須要相應的糾正措施.當兩個事務開始修改不一樣的表,而且以不一樣的順序訪問這些表時,就可能會由於互相等待而都不能再繼續執行.MySQL會當即發現這個狀況,而且取消(roll back)較小(smaller)的事務,而後容許其餘的事務繼續執行.若是死鎖發生的頻率比較高的惡化,你可能須要review代碼而後對SQL操做進行從新排序,或者將一個大的事務拆分紅幾個更小的事務.
InnoDB提供了一種可配置的鎖機制,它能夠顯著提升帶AUTO_INCREMENT列的SQL語句的擴展性和性能,這裏的SQL語句是指向表中添加數據的SQL語句.
術語:
INSERT INTO t1 (c1,c2) VALUES (1,'a'), (NULL,'b'), (5,'c'), (NULL,'d');複製代碼
innodb_autoinc_lock_mode
配置參數可選的配置爲0、一、2,分別表示traditional、consecutive、interleaved.
TO BE CONTINUED...