MySQL--InnoDB筆記

1.InnoDB存儲引擎 

1.1.InnoDB介紹 

        InnoDB是一個平衡了高可靠性和高性能的通用存儲引擎.在MySQL的5.6版本中,InnoDB是默認的MySQL存儲引擎. 除非你配置了不一樣的默認存儲引擎,不然不帶ENGINE=字句的CREATE TABLE語句將建立一個InnoDB的表.html

像mysql和INFORMATION_SCHEMA這些實現了MySQL內部結構的數據庫,仍是使用的MyISAM引擎.而且不能將它們中的受權表切換爲InnoDB.  

1.2.主要優點

  • DML(Data manipulation Language)遵循ACID模式,而且事務具備commit、rollback和crash-recovery的功能以保護用戶的數據.
  • 行級鎖(row-level lock)和Oracle風格的一致性讀(consistent reads)提高了多用戶的併發性和性能.
  • InnoDB表會根據主鍵在磁盤上排列數據以優化查詢.每個InnoDB表都有一個稱爲聚簇索引(clustered index)的主鍵索引來組織數據以減小主鍵查詢時的I/O操做.
  • 爲了維護數據的完整性,InnoDB支持外鍵(FOREGIN KEY)約束.有了外鍵,將檢查插入、更新和刪除操做,以確保它們不會致使不一樣表之間的不一致.

1.3.使用InnoDB的好處

        這裏只挑選了一些我的認爲對比較重要的點,更多細節請參考Benefits of Using InnoDB Tables.mysql

  • 故障恢復(crash recovery).無論你的服務器由於硬件仍是軟件的問題發生了故障,無論當時數據庫正在發生什麼,你不須要在重啓數據庫以後作任何特殊的操做.InnoDB的故障恢復機制會自動的完成故障前已經提交的修改,而故障前未提交的數據將被丟棄.
  • 緩存池(buffer pool):當數據被訪問時,InnoDB引擎會在主存中維護本身的緩存池來緩存表以及索引數據.頻繁使用的數據將直接從內存中訪問.該緩存能適用於多種不一樣類型的信息,並能提高處理速度.在一些專用的數據庫服務器上,一般超過80%的物理內存會分配給緩存池.
  • 外鍵(foreign keys):當把相關聯的數據拆分到不一樣的表中時,可使用外鍵.當修改或者刪除數據時,在其餘表的相關聯的數據將會被自動修改或刪除.當嘗試向一個二級表中插入數據而沒有修正主表中的數據時,髒數據將被自動剔除.
  • 當你爲你的數據庫中的每一個表設計了適當的主鍵列時,包含這些列的操做將自動被優化.當在WHERE語句、ORDER BY語句、GROUP BY語句和join操做中引用這些主鍵列時會變得很是快.
  • 插入、修改和刪除會被一種稱爲change buffering的自動機制優化.InnoDB不只容許併發的讀寫同一張表,它還會對修改進行緩存以提升磁盤的I/O效率.
  • 你能夠在對性能和可靠性產生很小影響的狀況下建立和刪除索引.

1.4.InnoDB表的最佳實踐

  • 爲每一張表指定一個主鍵.最好使用會被頻繁查詢的一列或多列,若是沒有明顯的主鍵也可使用一個被標記爲自增(auto-increment)的列.
  • 當基於指定的ID值從多個表中拉取數據時要使用join操做.爲提升性能,能夠將這些作join操做的列都定義爲外鍵,並在每一個表中都將這些列聲明爲相同的數據類型.定義爲外鍵能夠確保這些列會被索引化,這樣能夠提升性能.
  • 關閉自動提交(autocommit).一秒鐘幾百次提交可能影響性能(受限於你的存儲設備的寫入速度).
  • 將相關聯的DML操做放入事務中.
  • 不要使用鎖表(LOCK TABLES)語句.InnoDB能夠同時處理多個讀寫同一張表的會話而不會影響可靠性和高性能.爲了獨佔式的獲取一些列的寫入權限,可使用SELECT ... FOR UPDATE語法來鎖住你想要修改的列.
  • 開啓innodb_file_per_table選項,使一個表的數據和索引放入不一樣的文件中(也被稱爲獨立表空間),而不是放入系統表空間(system tablespace)中.這個設置會要求使用其餘的特性,好比表壓縮(compression)(默認開啓的).
  • 評估你的數據和訪問模式是否受益於InnoDB的表壓縮功能.
  • 使用 --sql_mode=NO_ENGINE_SUBSTITUTION參數運行你的服務器以防止表被不一樣的存儲引擎建立.

2.InnoDB和ACID

        ACID模式是一系列強調對業務數據和關鍵任務應用程序很重要的可靠性方面的數據庫設計原則.算法

  • A:atomicity,原子性.
  • C:consistency,一致性.
  • I:isolation,隔離性.
  • D:durability,持久性.

3.InnoDB多版本

3.1.多版本

        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

3.2.多版本和二級索引

       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都會到聚簇索引中查找記錄.

4.InnoDB內存結構

4.1.Buffer Pool

        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看成兩個子列表:

  • 在頭部,是一個存放最近被訪問的新頁(new "young" page)的子列表,new sublist.
  • 在尾部,是一個存放最近不多被訪問的老頁(old page)的子列表,old sublist.

下圖是Mysql官網中的buffer pool鏈表的結構圖


        這個算法使被頻繁訪問的pages處於new sublist中.Old sublist中存放不多使用的pages,這些pages是將被丟棄的候選pages.

默認狀況下,算法操做以下:

  • 3/8的buffer pool被分配給old sublist.
  • 中點(midpoint of the list)是new sublist的尾節點和old sublist的頭節點相遇的邊界處. 如上圖.
  • 當InnoDB將一個page讀入到buffer pool中時,它最初是被插入到中點(old sublist的頭節點).
  • 當訪問一個在old sublist中的page時,將使該page變得年輕(young),並將它移動到buffer pool的頭節點(也就是new sublist的頭節點).若是一個page是由於用戶操做而被讀取(例如一個query語句),那麼第一次訪問(first access)就會當即觸發並將該page變得年輕(young).若是一個page是由於預讀(read-ahead)操做而被讀取,那麼第一次訪問(first access)可能在該page被丟棄以前都不會發生.
  • 當數據庫運行時,那些在buffer pool中沒有被訪問的pages經過向隊列的尾節點移動來增長年齡(age),越靠近buffer pool的尾節點,年齡越大.在new sublist和old sublist中的pages的年齡都會由於其餘page變得年輕而增長,換句話來講就是每當有一個page變年輕了(該page被移動到buffer pool的頭結點),那麼new和old sublist中的其餘page都會依次向後移動.在old sublist中的page也會由於在中點插入頁而向尾節點移動.最終,當一個未被訪問的頁到了old sublist的尾節點時將被丟棄.

默認狀況下,經過query語句讀取的pages會立刻被移動到new sublist中,意味着他們能在buffer pool存活更長的時間.一個全表掃描(例如一次mysqldump操做,或者一個沒有where條件的select語句)會使大量的數據進入buffer pool中而且會丟棄相同數量的老數據,儘管這些新的數據將不會被再次使用.相似的還有經過預讀後臺線程加載的pages,在被訪問一次以後就會被放入new sublist的頭節點中,儘管以後可能並不會訪問這些pages.這些狀況將致使那些真正會被常常訪問的pages進入到old sublist,這樣他們就變成了可能被丟棄的對象.

Buffer pool配置

你能夠配置buffer pool的各個方面提升性能.

  • 理想狀況下,能夠 將buffer pool的大小設置爲一個儘量大的值,爲服務器上的其餘進程保留足夠的內存,以便在不過分分頁的狀況下運行.buffer pool越大,InnoDB越像一個內存數據庫,只從磁盤上讀取一次數據,以後的query操做都直接訪問內存.經過 innodb_buffer_pool_size來配置buffer pool的大小.
  • 在有充足內存的64位系統中,能夠將buffer pool分割成多個部分來減小
  • 併發操做對內存結構的競爭.參考 Configuring  Multiple Buffer Pool Instances.
  • 你能夠將常常被訪問的數據保持在內存中,而忽略掉那些在高峯時刻可能帶來大量不會常常被訪問的數據到buffer pool中的操做.參考 Making the Buffer Pool Scan Resistant.
  • 你能夠控制後臺刷新什麼時候發生而且是否根據workload來動態調整刷新頻率.參考 Configuring InnoDB Buffer Pool Flushing.
  • 您能夠控制什麼時候以及如何執行預讀請求,以異步地將pages預取到buffer pool中,以預期很快將須要這些pages.參考 Configuring InnoDB Buffer Pool Prefetching(Read-Ahead).
  • 能夠微調緩衝池刷新行爲的各個方面,以提升性能.參考 Fine-tuning InnoDB Buffer Pool Flushing.
  • 您能夠配置InnoDB如何保留當前buffer pool的狀態,以免服務器從新啓動後長時間預熱.參考 Saving and Restoring the Buffer Pool State.

4.2 Change Buffer

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值以下:

  • all:默認值,緩存inserts,delete-marking操做,和清除操做.
  • none:不緩存任何操做.
  • inserts:只緩存插入操做.
  • deletes:只緩存delete-marking操做.
  • changes:緩存inserts和delet-marking操做.
  • purges:緩存在後臺發生的物理刪除操做.

你能夠經過在MySQL的配置文件(my.cnf或者my.ini)中的 innodb_change_buffering 設置或者經過 SET GLOBAL 語句來進行動態配置.

配置Change Buffer最大大小

innodb_change_buffer_max_size參數容許配置change buffer佔buffer pool的最大百分比.默認是25,最大能設置爲50.

4.3 自適應Hash索引

基於觀察搜索的模式,來利用索引key的前綴來建立hash索引.這個前綴能夠是任意長度的,可能只有一個B-tree的的值會出如今hash索引中.Hash索引是針對那些常常被訪問的索引頁來創建的.

若是一個表的數據基本均可以在內存中匹配,那麼hash索引能夠提升查詢的速度.InnoDB有一種機制監控索引查詢.若是InnoDB以爲查詢能夠受益於建立hash索引,那麼它就會自動創建hash索引.

在某些工做負載下,hash索引查詢的速度提高遠大於監控索引查詢和維護hash索引結構的額外工做.在過量工做負載的狀況下,訪問自適應hash索引有時可能會成爲競爭的一個源頭,例如多個併發鏈接(我理解當存在併發狀況的時候,只有一個線程會去建立hash索引,所以會存在競爭).LIKE語句不會使用hash索引.在MySQL 5.6版本使得禁用自適應hash索引比以前的版本更加合適.

4.4 Log Buffer

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的刷新頻率.

5.InnoDB磁盤(On-Disk)結構

過長的事務:當事務開啓時,系統會在事務開始時保存一份數據的快照,所以若是存在大量的插入、修改或刪除操做使,會致使大量的開銷.

  • 當事務完成時,始終進行COMMIT或ROLLBACK操做.
  • ROLLBACK是相對昂貴的操做,最好使大部分的修改能成功commit,而進行較少的rollback.
  • 對於進行大量插入操做的語句,應該週期性的進行commit操做,避免一次性commit很長時間(例如幾個小時).若是出錯,最好truncate表而後從頭開始執行,而不要執行rollback操做.

太短的事務:太短的事務會致使頻繁的I/O操做,也會產生大量開銷.

  • 對InnoDB表的大部分操做,最好設置autocommit = 0.從效率的角度來看,這樣能夠避免沒必要要的I/O操做.從安全的角度來講,這樣也能夠在發生錯誤的時候執行ROLLBACK進行回滾.
  • 即便是一個SELECT語句也會建立一個事務,所以在最後也須要加上COMMIT操做或者關閉mysql session.


處理死鎖(Deadlocks):對InnoDB表來講,deadlock並非一個嚴重的問題,一般也不須要相應的糾正措施.當兩個事務開始修改不一樣的表,而且以不一樣的順序訪問這些表時,就可能會由於互相等待而都不能再繼續執行.MySQL會當即發現這個狀況,而且取消(roll back)較小(smaller)的事務,而後容許其餘的事務繼續執行.若是死鎖發生的頻率比較高的惡化,你可能須要review代碼而後對SQL操做進行從新排序,或者將一個大的事務拆分紅幾個更小的事務.

5.1 InnoDB中處理AUTO_INCREMENT

InnoDB提供了一種可配置的鎖機制,它能夠顯著提升帶AUTO_INCREMENT列的SQL語句的擴展性和性能,這裏的SQL語句是指向表中添加數據的SQL語句.

術語:

  • "INSERT-like"語句:全部能在表中產生新行的語句,包括INSERT、INSERT ... SELECT、REPLACE、REPLACE ... SELECT和LOAD DATA.包括"simple-inserts"、"bulk-inserts"和"mixed-mode inserts".
  • "Simple inserts": 可以提早知道將被插入的行的數量的語句.包括單行和多行的INSERT和REPLACE語句(不包含子查詢),不包括INSERT ... ON DUPLICATE KEY UPDATE.
  • "bulk inserts":沒法提早知道將被插入行的數量的語句.包括INSERT ... SELECT、REPLACE ... SELECT和LOAD DATA,不包括簡單INSERT語句.在處理每一行時,InnoDB爲AUTO_INCREMENT列分配一個新的值.
  • "mixed-mode inserts":在"simple inserts"的語句中一部分(不是所有)指定了auto-increment的值.以下所示.

INSERT INTO t1 (c1,c2) VALUES (1,'a'), (NULL,'b'), (5,'c'), (NULL,'d');複製代碼

innodb_autoinc_lock_mode配置參數可選的配置爲0、一、2,分別表示traditional、consecutive、interleaved.

  • traditional鎖模式:在這種模式下,全部的'INSERT-like'語句獲取一個特殊的table-level AUTO-INC鎖來進行帶AUTO-INCREMENT列的插入.這個鎖一般會持有到語句結束的時候(不是事務結束的時候)以保證以可預測和可重複的順序爲給定的insert語句序列分配自動增量值,同事也保證爲語句分配的自動增量值是連續的.





TO BE CONTINUED...

相關文章
相關標籤/搜索