《MySQL技術內幕-InnoDB存儲引擎》整理6-事務

一、認識事務

1、概述

  • A(Atomicity)原子性:原子性是指數據庫事務是不可分割的工作單位,只有使事務中所有的數據庫操作都執行成功,纔算整個事務成功,事務中任何一個SQL語句執行失敗,已經執行成功的SQL語句也必須撤銷,數據庫狀態應該回退到執行事務前的狀態。
  • C(Consistency)一致性:一致性指事務將數據庫從一種狀態轉變爲下一種一致性狀態,在事務開始之前和事務結束之後,數據庫的完整性約束沒有被破壞。
  • I(Isolation)隔離性:事務的隔離性要求每個讀寫事務的對象對其他事務的操作對象能夠相互分離,即該事務提交前對其他事務都不可見,通常由鎖來實現。
  • D(Durability)持久性:事務一旦提交,其結果就是永久性的,即使發生宕機等故障,數據庫也能將數據恢復

2、分類

從事務理論的角度,可以將事務分爲以下幾種類型:

  • 扁平事務:所有事務中最簡單的一種,也是使用最爲頻繁的一種,所有操作都處於同一層級,由Begin Work開始,Commit Work或者RollBack Work結束
  • 帶有保存點的扁平事務:除了支持扁平事務支持的操作外,允許在事務執行過程中回滾到同一事務中較早的一個狀態。保存點用來通知系統應該記住事務當前的狀態,以便發生錯誤時,事務能回到保存點當時的狀態,保存點通過Save Work函數來建立
  • 鏈事務:保存點模式的變種,其思想是在提交一個事務時,釋放不需要的數據對象,將必要的處理上下文隱式地傳給下一個要開始的事務,即將提交事務操作和開始下一個事務操作合併爲一個原子操作。鏈式事務只能恢復到最近一個保存點,在執行Commit後即釋放了當前事務所持有的鎖
  • 嵌套事務:是一個層次結構框架,由一個頂層事務控制着各個層次的事務,頂層事務之下嵌套的事務被稱爲子事務,控制着每一個局部的變換
  • 分佈式事務:通常是一個在分佈式環境下運行的扁平事務,因此需要根據數據所在位置訪問網絡中的不同節點。

InnoDB存儲引擎支持扁平事務,帶有保存點的事務,鏈事務和分佈式事務,不支持嵌套事務

二、事務的實現

原子性,一致性,持久性通過數據庫的redo log和undo log來完成,redo log稱爲重做日誌,來保證事務的原子性和持久性,undo log用來保證事務的一致性。redo恢復提交事務修改的頁操作,而undo回滾行記錄到某個特定版本。redo通常是物理日誌,記錄的是頁的物理修改操作;undo是邏輯日誌,根據每行記錄進行記錄

1、redo

1、基本概念

重做日誌用來實現事務的持久性,其由兩部分組成:內存中的重做日誌緩衝,其是易失的;重做日誌文件,其是持久的。InnoDB是事務的存儲引擎,當事務提交時,必須先將該事務的所有日誌寫入到重做日誌文件進行持久化,待事務的Commit操作完成纔算完成,這裏的日誌爲redo log和undo log。

爲了確保每次日誌都寫入重做日誌文件,在每次將重做日誌緩衝寫入重做日誌文件後,InnoDB存儲引擎都需要調用一次fsync操作,fsync的效率取決於磁盤的性能。參數innodb_flush_log_at_trx_commit用來控制重做日誌刷新到磁盤的策略

  • 默認值爲1表示事務提交時必須調用一次fsync操作;
  • 0表示事務提交時不進行寫入重做日誌操作,重做日誌操作由master thread完成,其會每1秒進行一次重做日誌操作;
  • 2表示事務提交時將重做日誌寫入重做日誌文件,但僅寫入文件系統緩衝,不進行fsync操作

Mysql數據庫中還有一種二進制日誌,其用來進行Point-In-Time的恢復以及主從複製環境的建立。它是一種邏輯日誌,記錄的是SQL語句。二進制日誌僅在事務提交時記錄,對於每一個事務僅包含對應事務的一個日誌。

2、log block

在InnoDb存儲引擎中,重做日誌都是以512字節進行存儲的,即重做日誌緩存和重做日誌文件都是以block進行保存的,稱之爲重做日誌塊,每塊大小爲512字節。重做日誌塊除了日誌本身,還包括日誌塊頭和日誌塊尾兩部分組成。日誌塊頭中的信息可以用來標記一些塊的信息

3、log group

log group稱爲重做日誌組,其中有多個重做日誌文件,它只是一個邏輯上的概念,並沒有一個實際存儲的物理文件來表示。它由多個重做日誌文件組成,每個log group中的重做日誌文件大小是相同的,且其總大小隻能小於4GB,後提高爲512GB。log group中的第一個redo log file,其前2KB的部分保存4個512字節大小的塊,其中會包含一些檢查點信息,對於恢復操作來說至關重要

4、重做日誌格式

InnoDB存儲引擎的存儲是基於頁的,因此重做日誌格式也是基於頁的,雖然有着不同的重做日誌格式,但是它們有着通用的頭部格式:

  • redo_log_type:重做日誌的類型
  • space:表空間ID
  • page_no:頁的偏移量

5、LSN

LSN是Log Sequence Number的縮寫,其代表的是日誌序列號,在InnoDB存儲引擎中共佔8個字節,並且單調遞增,其代表的含義有:①重做日誌寫入的總量;②CheckPoint的位置;③頁的版本

6、恢復

InnoDB存儲引擎在啓動時不管上次數據庫運行時是否正常關閉,都會嘗試進行恢復操作。由於CheckPoint表示已經刷新到磁盤頁上的LSN,因此在恢復過程中僅需恢復CheckPoint開始的日誌部分。InnoDB存儲引擎的重做日誌是物理日誌,因此恢復速度較二進制日誌快得多。

2、undo

1、基本概念

undo信息可以用來進行信息的回滾操作,它存放在數據庫內部的一個特殊段中,這個段稱爲undo段。undo段位於共享表空間中。undo並不是將數據庫物理的恢復到執行語句或事務之前的樣子,而是將數據庫邏輯的恢復到原來的樣子。

undo可以實現多版本併發控制,即用戶讀取一行記錄時,若該紀錄被其他事務佔用,當前事務可以讀取undo讀取之前的行版本信息,以此實現非鎖定讀取。此外,undo log的產生會伴隨着redo log的產生,這也是undo log需要持久性保護的原因

2、undo存儲管理

InnoDB存儲引擎對undo的管理同樣採用段的方式,InnoDB存儲引擎有rollback segment,每個回滾段種記錄了1024個undo log segment,而在每個undo log segment段種進行undo頁的申請。從InnoDB1.1版本開始,InnoDB最大支持128個rollback segment,這些rollback segment都存儲於共享表空間中,從InnoDB1.2版本開始,又可以通過以下設置對rollback segment進行進一步設置:

  • innodb_undo_directory:用於設置rollback segment文件所在的路徑,默認值爲.表示InnoDB存儲引擎的目錄
  • innodb_undo_logs:用來設置rollback segment的個數,默認值爲128
  • innodb_undo_tablespaces:用來設置構成rollback segment文件的數量

3、undo log格式

InnoDB存儲引擎中,undo log分爲insert undo log和update undo log。Insert undo log開始的前兩個字節next記錄的是下一個undo log的位置,尾部的兩個字節記錄的是undo log的開始位置,type_no記錄事務的ID,table_id記錄undo log所對應的表對象,其餘部分記錄了所有主鍵的列和值,rollback時可以根據這些值找到具體的記錄,在進行刪除操作。update undo log記錄的是對delete和update操作產生的undo log,由於該undo log可能需要提供MVCC機制,所以不能在事務提交時刪除,它會在事務提交時放入undo log鏈表,等待purge線程進行最後的刪除。

4、查看undo信息

Oracle和MSSQL可以通過內部的數據字典來查看當前的undo信息,而InnoDB則不行,只能通過原理和個人進行進行判斷

3、purge

delete和update操作不能直接刪除原有的數據,真正的刪除操作被延時了,並最終在purgr操作中完成。當某行數據不被任何其他事務引用時,則可以進行真正的刪除操作。InnoDB存儲引擎有一個history表,它根據事務提交的順序,將undo log進行鏈接。當清理髮生時,InnoDB存儲引擎首先從history list中找到第一個需要被清理的記錄,清理後再繼續尋找可清理的對象,直至最尾端,這也是undo page可以被重用的原因

innodb_purge_batch_size用來設置每次purge操作時需要清理的undo page數量,1.2版本之後默認值爲300,其值越大回收的undo頁越多,CPU和磁盤IO的操作也會越多,性能也會隨之下降。innodb_max_purge_lag用來控制history list的長度,其長度越大,InnoDB存儲引擎的壓力也會越高,其默認值爲0表示不對history list做任何限制,當大於0時,就會延遲DML操作。延遲算結果爲(length(history_list)-innodo_max_purge_lag)*10-5,單位爲毫秒,且作用對象是行,即更新5行數據時,總延遲時間爲5個單位,每次purge操作後會重新計算延遲值。innodb_max_purge_lag_delay用來控制delay的最大毫秒數,以避免延遲過大導致的無限制等待

4、group commit

爲了提高磁盤fsync的效率,當前數據庫提供了group commit的供,即一次fsync可以刷新確保多個事務日誌被寫入文件。在開啓二進制日誌後,InnoDB存儲引擎的group commit功能會失效,從而導致性能的下降,這是由於開啓二進制日誌後,爲了保證存儲引擎層中的事務與二進制日誌的一致性,二者使用了二階段事務,導致每個步驟都需要進行一次fsync。

Mysql5.6以後,Binary Log Group Commit(BLGC)的實現方式解決了上述問題,其實現方式是將事務提交的過程分爲幾個步驟來完成。在Mysql數據庫上層進行提交時首先按順序將其放入一個隊列中,隊列的第一個事務稱爲leader,其他事務稱爲follower,leader控制着follower的行爲,BLGC的步驟分爲如下三個階段:

  • Flush階段,將每個事務的二進制日誌寫入內存中;
  • Sync階段,將內存中的二進制日誌刷新到磁盤,若隊列中有多個事務,那麼僅以此fsync操作就完成了二進制日誌的寫入;
  • Commit階段,leader根據順序調用存儲引擎層事務的提交,而InnoDB存儲引擎是支持group commit的

當有一組事務在進行Commit階段時,其他新事物可以進行Flush階段,從而使group commit不斷生效,且事務越多,group commit的效果越明顯,數據庫性能的提升也就越大。

三、事務控制語句

在Mysql命令行的默認設置下,事務都是自動提交的,因此要顯式地開啓一個事務需要使用命令Begin、Start Transaction,或者執行命令Set AutoCommit=0,禁用當前的自動提交。部分控制語句如下:

  • Start Transaction|Begin:顯式地開啓一個事務
  • Commit:提交事務
  • RollBack:結束用戶的事務,並撤銷正在進行的所有未提交的修改
  • SavePoint identifier:在事務中創建一個保存點,一個事務中可以有多個SavePoint
  • Release SavePoint identifier:刪除一個事務的保存點
  • RollBack To [SavePoint] identifier:把事務回滾到標記點
  • Set Transaction:設置事務的隔離級別

需要注意的是RollBack To SavePoint並不是真正地結束一個事務,因此需要顯式地運行Commit或者RollBack命令

四、隱式提交的SQL語句

以下語句執行後會有一個隱式地Commit操作:①DDL語句;②修改Mysql架構的操作;③管理語句

需要注意在MSSQL中,DDL操作是可以回滾的,而InnoDB存儲引擎不行

五、對於事務操作的統計

InnoDB存儲引擎的應用在考慮QPS的同時,應該關注每秒事務處理的能力(TPS)。計算TPS的方法是(com_commit+com_rollback)/time,其計算前提是所有事務都是顯式提交的,隱式提交及回滾不會計算到com_commit和com_rollback變量中

六、事務隔離級別

SQL標準定義的四個隔離級別爲:Read UnCommitted、Read Committed、Repeatable Read、Serializable。隔離級別越低,事務請求的鎖越少或保持鎖的時間越短。InnoDB存儲引擎默認支持的隔離級別是Repeatable Read,且使用Netx-Key Lock算法避免幻讀,這與其他數據庫不同,也就是說InnoDB存儲引擎在默認的Repeatable Read隔離級別下,已經能夠完全保證事務隔離性的要求,達到SQL標準的Serializable隔離級別

在Serialiable的事務隔離級別,InnoDB存儲引擎會對每個Select語句自動加上Lock In Share Mode,即爲每個讀取操作加一個共享鎖,因此該隔離級別下,對一致性的非鎖定讀不再予以支持。Serialiable的事務隔離級別主要用於InnoDB存儲引擎的分佈式事務。

在Read Committed隔離級別下,除了唯一性的約束檢查以及外鍵約束使用gap lock,InnoDB存儲引擎不會使用gap lock鎖算法,在Mysql5.1中,該事務隔離級別只能工作在replication二進制日誌爲Row的格式下,Mysql5.1版本後,因爲支持了Row格式的二進制日誌記錄格式,因而避免了一些不同步現象的產生。此外,即使不使用Read Committed隔離級別,也應當考慮將二進制日誌的格式更換爲Row,以進一步保證數據的同步

七、分佈式事務

1、Mysql數據庫分佈式事務

InnoDB存儲引擎提供了對XA事務的支持,並通過XA事務來支持分佈式事務的實現。分佈式事務是指允許多個獨立的事務資源參與到一個全局事務中。在使用分佈式事務時,InnoDB存儲引擎的事務隔離級別必須設置爲Seaializable

XA事務允許不同數據庫之間的分佈式事務,XA事務由一個或多個資源管理器、一個事務管理器以及一個應用程序組成。資源管理器提供訪問事務資源的方法,通常一個數據庫就是一個資源管理器,事務管理器協調參與全局事務的各個事務,應用程序定義事務的邊界,指定全局事務中的操作。

分佈式事務使用兩段式提交的方法,在第一階段,所有參與全局事務的節點都開始準備,告訴事務管理器它們準備好提交,第二階段事務管理器告訴資源管理器是執行Commit還是RollBack。與本地事務不同的是,分佈式事務需要多一次的準備操作,所有節點同意後,再進行Commit或者RollBack操作。

2、內部XA事務

在Mysql數據庫中還存在另一種分佈式事務,其存在於存儲引擎與插件,或者是存儲引擎與存儲引擎之間,稱爲內部XA事務。最常見的內部XA事務存在於binlog與InnoDB存儲引擎之間,由於複製的需要,大多數數據庫都開啓了binlog功能,在事務提交時,先寫二進制日誌,再寫InnoDB存儲引擎的重做日誌,兩者必須保持同步。事務提交時,InnoDB存儲引擎會先做一個Prepare操作,將事務的xid寫入,接着進行二進制日誌的寫入,如在提交前宕機,重啓後數據庫會檢查準備的UXID事務是否已經提交,如沒有則存儲引擎會再進行一次提交操作

八、不好的事務習慣

避免在循環中提交;避免使用自動提交;避免使用自動回滾

九、長事務

對於較長的事務,有時可以通過轉換爲小批量的事務來進行處理,當事務發生錯誤時,只需要回滾一部分數據

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章