MySQL技術內幕-InnoDB存儲引擎-第七章、事務

事務

事務:事務會把數據庫從一種一致狀態轉化爲另外一種一致狀態,在數據庫提交工作時,可以確保要麼所有修改已經保存了,要麼所有修改都不保存。

事務的四個特性(ACID):

  • 原子性
  • 一致性
  • 隔離性
  • 持久性

第六章介紹的鎖談論了事務如何實現隔離性的,本章主要關注原子性這個概念

一、認識事務(要麼所有修改,要麼都不保存)

1、概述

事務可以由一條簡單的SQL語句組成,也可以由一組複雜的SQL語句組成。事務是訪問並更新數據庫中各種數據項的一個程序執行單元。在事務中的操作要,餓都成功,要麼都不做。

Oracle數據庫默認隔離級別爲READ COMMITTED,不滿足隔離性的要求,而InnoDB默認的隔離級別是READ REPEATABLE,完全遵循和滿足事務的ACID特性。

原子性(事務中的操作要麼全部成功,要麼全部失敗)

取錢例子

  • 登陸ATM取錢,驗證密碼
  • 從遠程銀行數據庫中,取得賬戶信息
  • 用戶在ATM上輸入想提取的金額
  • 從遠程銀行的數據庫中更新賬戶信息
  • ATM出款
  • 用戶取錢

整個取錢過程應該被看作原子操作,即要麼都做,要麼都不做。

原子性指整個數據庫事務是一個不可分割的工作單元,只有使事務中所有的數據庫操作都執行成功,纔算整個事務成功。

一致性(事務將數據庫從一種狀態轉變爲下一種一致的狀態)

一致性指的是事務將數據庫從一種狀態轉變爲下一種一致的狀態
如果事務中的某個動作失敗了,系統可以自動撤銷事務,返回初始化的狀態。

隔離性(要求每個讀寫事務的對象對其他事務的操作能相互隔離)

隔離性要求每個讀寫事務的對象對其他事務的操作對象能相互隔離,即該事務的提交對其他事務都是不可見的,通常使用鎖來實現。

持久性(事務一旦提交,結果就是永久性的)

事務一旦提交,結果就是永久性的。即使發生宕機等故障。數據庫也能將數據恢復好。

2、分類

從事務理論的角度來說,可以把事務分爲下面幾種類型:

  • 扁平事務
  • 帶有保存點的扁平事務
  • 鏈事務
  • 嵌套事務
  • 分佈式事務

扁平事務(最簡單、使用最頻繁)

在扁平事務中,所有操作都處於同一層次。
扁平事務由begin work開始,由commit work或者rollback work結束,其間的操作是原子的。要麼都執行,要麼都回滾。
在這裏插入圖片描述

扁平事務的主要限制是不能提交或者回滾事務的某一部分。或分幾個步驟提交。

下面的旅行度假例子使用扁平式事務就不可以完成:

  • begin work
  • 預定杭州到上海的高鐵
  • 上海浦東國際機場坐飛機,預定去米蘭的航班
  • 在米蘭轉火車前往佛羅倫薩,預定去佛羅倫薩的火車。

如果到達米蘭太晚了,沒有當天火車,這時希望在米蘭住一晚,第二天去佛羅倫薩,如果事務是扁平事務,則要回滾之前的操作,這個代價有點大。

因此就出現了帶有保存點的扁平事務

帶有保存點的扁平事務:除了支持扁平事務支持的操作外,允許在事務執行過程中回滾到同一事務中較早的一個狀態。這是因爲,某些事務可能在執行過程中出現的錯誤並不會導致所有的操作都無效。

保存點:用來通知系統應該記住事務當前的狀態,以便當之後發生錯誤的時候,事務能回到保存點當時的狀態。
可以根據邏輯決定回到最近的一個保存點還是更早的保存點。

保存點在事務內部是遞增的。rollback是不影響保存點計數的。如下圖

在這裏插入圖片描述

鏈事務:

  • 帶有保存點的扁平事務,當發生系統崩潰的時候,所有保存點都將消息,因爲其保存點是易失的。這就意味着當進行恢復的時候,事務需要從開始處重新執行,而不能從最近的一個保存點繼續執行。
  • 鏈事務的思想是:在提交事務的時候,釋放不需要的數據對象,將必要的處理上下文隱式傳給下一個要開始的事務。注意:提交事務操作和開始下一個事務操作將合併爲一個原子操作。在這裏插入圖片描述
    嵌套事務:
    嵌套事務是一個層次結構框架,由一個頂層事務控制各個層次的事務。頂層事務之下嵌套的事務被稱爲子事務。

在這裏插入圖片描述
嵌套事務的定義:

  • 嵌套事務是由若干事務組成的一棵樹,子樹既可以是嵌套事務,也可以是扁平事務。
  • 處在葉節點的事務是扁平事務,但是每個事務從根到葉節點的距離可以是不同的。
  • 位於根節點的事務稱爲頂層事務,其他事務爲子事務,事務的前驅稱爲父事務,事務的下一層稱爲兒子事務
  • 子事務既可以提交也可以回滾,但是他的操作不是立馬生效,除非其父事務提交。
  • 樹中的任何一個事務的回滾都會引起它的所有子事務一同回滾,故子事務保留ACI特性,不具有D的特性。

實際的工作是交給葉子節點來完成的,即只有葉子節點的事務才能訪問數據庫、發送消息、獲取其他類型的資源。即高層的事務僅負責邏輯控制。

在這裏插入圖片描述

分佈式事務:

通常是一個在分佈式環境下運行的扁平事務,因此需要根據數據所在位置訪問網絡中的不同節點。

假設一個用戶在ATM機進行銀行的轉賬操作,例如持卡人從招商銀行的儲蓄卡轉賬10000到工商銀行的儲蓄卡,在這種情況下,可以將ATM視爲節點A,招商銀行的後臺數據庫視爲節點B,工商銀行的後臺數據庫視爲C,這個轉賬的操作可分解爲以下的步驟:

  • 節點A發出轉賬命令
  • 節點B執行儲蓄卡中的餘額減去10000
  • 節點C執行儲蓄卡中的餘額加上10000
  • 節點A通知用戶操作完成或者節點A通知用戶操作失敗。

這裏需要使用分佈式事務,因爲節點A不能通過調用一個數據庫就完成,其中需要訪問網絡中的兩個節點的數據庫。而在每個節點的數據庫執行的事務操作又都是扁平的,對於分佈式事務,同樣需要滿足ACID特性,要麼都發生,要麼都失敗。

二、事務的實現(原子性、一致性、持久性通過數據庫的redo log、undo log來完成)

事務的隔離性由鎖來實現。
原子性、一致性、持久性通過數據庫的redo log、undo log來完成,redo log稱爲重做日誌,用來保證事務的原子性和持久性。undo log用來保證事務的一致性。

有的DBA認爲undo是redo的逆過程,其實不然,redo和undo的作用都可以視爲一種恢復操作,redo恢復提交事務修改的頁操作,而undo回滾行記錄到某個特定版本。因此兩者記錄的內容不同,redo通常是物理日誌,記錄的是頁的物理修改操作,undo是邏輯日誌,根據每行記錄進行記錄。

1、redo

1、基本概念

重做日誌用來實現事務的持久性,即事務ACID中的D。
它由兩部分組成:

  • 內存中的重做日誌緩衝,易失的
  • 重做日誌文件,持久的

InnoDB通過Force Log at Commit機制實現事務的持久性,即當事務提交的時候,必須先將該事務的日誌寫入到重做日誌文件進行持久化。待事務的COMMIT操作完成纔算完成

redo log和undo log,redo log用來保證事務的持久性,undo log用來幫助事務回滾及MVCC的功能,redo log基本上是順序寫的,在數據庫允許的時候不需要對redo log的文件進行讀取操作,而undo log需要進行隨機讀寫的。

爲了保證每次日誌肉寫入重做日誌文件,在每次將重做日誌緩衝寫入重做日誌文件之後,InnoDB存儲引擎都需要調用一次fsync操作,由於重做日誌文件打開並沒有使用O_DIRECT選項,因此重做日誌緩衝先寫入文件系統緩衝,爲了確保日誌文件寫入磁盤,必須進行一次fsync操作,由於fsync的效率取決於磁盤的性能,因此磁盤的性能決定了事務提交的性能,也就是數據庫的性能。

是否每次事務提交是都進行一次fsync操作可以通過參數innodb_flush_log_at_trx_commit控制:

  • 參數innodb_flush_log_at_trx_commit用老枝重做日誌刷新到磁盤策略。
  • 默認爲1,表示事務提交的時候必須調用一次fsync。
  • 還可以設置爲0和2。
  • 0表示事務提交的時候不進行寫入重做日誌操作,這個操作僅在master thread中完成,master thread會1s進行一次重做日誌的fsync操作。
  • 2表示事務提交的時候將重做日誌寫入重做日誌文件。但是僅僅寫入文件系統的緩存中,不進行fsync操作,所以數據庫宕機,數據不會丟失,因爲在文件系統緩存中,如果操作系統宕機,數據會丟失

執行CALL p_load(500000)會向表中插入50萬行的記錄,並執行50w次的fsync操作,下面是插入50w條記錄所需的時間即差不多兩分鐘:
在這裏插入圖片描述
將參數innodb_flush_log_at_trx_commit設置爲0的情況:
在這裏插入圖片描述
可以看到設置成0之後,插入50w行記錄的時間縮短爲13.9s。差不多是之前的12%。說明後者大大減少了fsync的次數,提高了數據庫執行的性能。

在這裏插入圖片描述

binlog(二進制日誌,和redo功能差不多,但是這是所有引擎都有的)

用來進行POINT-IN-TIME的恢復以及主從複製環境的建立。
從表面看其和重做日誌非常相似,都是記錄對於數據庫操作的日誌, 然而,從本質上看,兩者有着很大的不同。

  • 重做日誌是InnoDB存儲引擎層產生的
  • 二進制日誌是MySQL上層產生,所以任何存儲引擎對於數據庫的更改都會產生二進制日誌
  • 二進制日誌是一種邏輯體制,記錄的是SQL語句,重做日誌是物理格式日誌,記錄的是對於每個頁的修改
  • 二進制日誌只在事務提交完成後進行一次寫入,而在InnoDB存儲引擎的重做日誌在事務進行中不斷的被寫入,這表現爲日誌並不是隨事務提交的順序進行寫入的。
    在這裏插入圖片描述

2、log block

在InnoDB中,重做日誌都是以512字節進行存儲的,這意味着重做日誌緩存、重做日誌文件都是以塊的方式進行保存的,稱之爲重做日誌塊。

如果一個頁產生的重做日誌數量大於512字節,那麼需要分割爲重做日誌塊進行存儲。此外,由於重做日誌塊的大小和扇區大小一樣,都是512字節,因此重做日誌的寫入可以保證原子性,不需要doublewrite技術。

重做日誌除了日誌本身之外,還由日誌塊頭(12字節)及日誌塊尾(8字節)兩部分組成。所以每個重做日誌塊實際可以存儲的大小爲492字節。

在這裏插入圖片描述
在這裏插入圖片描述

3、log group(由多個重做日誌文件組成)

log group由多個重做日誌文件組成,每個log group中的日誌文件大小是相同的。InnoDB1.2之前重做日誌文件大小要小於4GB,從InnoDB1.2開始,重做日誌文件總大小的限制提高爲512GB。

重做日誌文件中存儲的就是之前logBuffer中的log block。

在InnoDB存儲引擎執行的過程中,log buffer根據一定的規則將內存中的log block刷新到磁盤。這個具體規則是:

  • 事務提交的時候
  • 當log buffer中有一般的內存空間已經被使用時
  • log checkpoint時

4、重做日誌格式

在這裏插入圖片描述

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

redo log body的部分,根據重做日誌類型的不同,會有不同的存儲內容。例如:頁上記錄的插入和刪除,

在這裏插入圖片描述
在InnoDB1.2的時候,一共有51種重做日誌類型

5、LSN(Log Sequence Number日誌序列號)

LSN在redo log中有,在頁中也有。redo log記錄的是每個頁的日誌,所以LSN應該就是對應於1個頁的。

佔用8字節,單調遞增。LSN表示的含義有:

  • 重做日誌寫入的總量
  • checkpoint的位置
  • 頁的版本

LSN表示事務寫入重做日誌的字節的總量,例如當前重做日誌LSN爲1000,有1個事務寫了100字節的重做日誌,那麼LSN就變成了1100.

LSN不僅記錄在重做日誌中,還存在於每個頁中。在每個頁的頭部有一個值FILE_PAGE_LSN,記錄了該頁的LSN。表示最後刷新時候的LSN的大小。因爲重做日誌記錄的是每個頁的日誌,因此頁中的LSN用來判斷是否需要進行恢復操作。例如,頁P1的LSN爲10000,數據庫啓動的時候,InnoDB監測到寫入重做日誌的LSN爲13000,並且事務已經提交,那麼數據庫需要進行恢復操作。將重做日誌應用到P1中。
在這裏插入圖片描述

三種LSN:

  • Log Sequence number:表示當前的LSN
  • Log flushed up to:表示刷新到重做日誌文件的LSN
  • Last checkpoint at:表示刷新到磁盤的LSN。

雖然上面的Log Sequence number和Log flushed up to兩個LSN是相同的,但是在實際生產環境下,該值可能是不同的,因爲在一個事務中從日誌緩衝刷新到重做日誌文件並不是只是在事務提交的時候發生的,每秒都會有從日誌緩衝刷新到重做日誌文件的動作。

在這裏插入圖片描述

6、恢復

InnoDB存儲引擎啓動時候會進行恢復操作,不管數據庫是否正常關閉,因爲重做日誌記錄的是物理日誌,所以恢復的速度要比邏輯日誌如二進制日誌快很多。

checkpoint表示已經刷新到磁盤頁的LSN,因此開始恢復過程中僅僅需要恢復checkpoint開始的日誌部分。
比如下面的例子,當數據庫在checkpoint的LSN爲10000的時候發生了宕機,恢復操作僅恢復LSN 10000-13000範圍內的日誌。

在這裏插入圖片描述

對於一個Insert,重做日誌記錄內容大致如下:

  • 表:create table t(a int,b int,primary key(a),key(b));
  • 執行SQL語句:insert into t select 1,2;
  • 需要對索引頁和輔助頁進行操作,記錄的重做日誌大致如下:
    在這裏插入圖片描述
    如果涉及B+樹的split,可能會有更多的頁需要記錄日誌。

2、undo(用於回滾、是邏輯日誌、用於MVCC、記錄不同版本的快照?)

1、基本概念(數據庫進行修改會產生redo和一定量的undo)

重做日誌記錄了事務的行爲,可以很好的通過其對頁進行“重做”操作。但是事務還需要進行回滾,就需要undo。因此在對數據庫進行修改的時候,InnoDB存儲引擎不但會產生redo,還會產生一定量的undo。如果回滾,利用undo信息即那個數據回滾到修改之前的樣子。

redo存放在重做日誌文件中,與redo不同,undo存放在數據庫內部的一個特殊段中,這個段叫做undo段,undo段在共享空間(ibdata)中。

對undo的誤解:
誤解: undo用於將數據庫物理的恢復到執行語句或事務之前的樣子,但是實施並非如此,undo是邏輯日誌,因此只是將數據庫邏輯的恢復到原來的樣子。所有修改操作都被邏輯取消了。但是數據結構和頁本身在回滾之後可能大不相同,不能將一個頁恢復到事務開始的樣子,因爲在這過程中,雖然這個事務回滾,但是可能有其他事務修改了這個頁。

例子

用戶在執行一個insert 10w條記錄的事務,這個事務會導致分配一個新的段,即表空間會增大。在用戶執行rollback的時候,會將插入的事務進行回滾,但是表空間的大小並不會因此而收縮,因此當InnoDB引擎回滾的時候,實際上做的是與之相反的工作。對於每個insert,InnoDB存儲引擎會完成一個DELETE,對於每個DELETE,InnoDB會執行一個insert,對於一個update,InnoDB會執行一個相反的update,將修改前的行放回去。

undo的另外一個作用是MVCC,在InnoDB存儲引擎中MVCC的實現是通過undo來完成,當用戶讀取一行記錄的時候,如果該記錄已經被其他事務佔用,當前事務可以通過undo讀取之前的行版本信息,一次實現非鎖定讀取。

2、undo存儲管理

InnoDB存儲引擎對undo的管理同樣採用段的方式,InnoDB存儲引擎有rollback segment即回滾段,每個回滾段中記錄了1024個undo log segment,而在每個undo log segment段中進行undo頁的申請。

在InnoDB1.1之前只有一個rollback segment,也就是說可以同時支持的在線事務限制爲1024.在InnoDB1.1之後最大支持128個rollback Segment,所以可以同時支持在線事務的限制提高到了128*1024.

需要注意的是,事務在undo log segment分配頁並寫入到undo log的這個過程同樣需要寫入重做日誌,當事務提交時,InnoDB存儲引擎會做以下兩件事情:

  • 將undo log放入列表,供以後的purge操作
  • 判斷undo log所在的頁是否可以重做,若可以分配給下個事務使用。

事務提交之後並不難馬上刪除undo log即undo log所在的頁,因爲可能還有其他事務需要通過undo log來得到行記錄之前的版本。是否可以刪除undo log和undo log所在的頁由purge線程來判斷。

在InnoDB存儲引擎的設計中對undo頁可以進行重用,具體來說,當事務提交的時候,首先將undo log放在鏈表中,然後判斷undo頁的使用空間是否小於3/4,如果是,則表示該頁可以被重用,之後新的undo log記錄在當前undo log的後面,由於存放undo log的列表是以記錄進行組織的,而undo頁可能存放着不同事務的undo log,因此purge操作需要設計磁盤的離散讀取操作。

可以使用命令show engine innodb status查看鏈表中undo log的數量。

3、undo log格式(insert undo log和update undo log)

undo log可以分爲:

  • insert undo log
  • update undo log

insert undo log指的是insert操作中產生的undo log。因爲insert只對事務本身可見,對其他事務不可見,所以undo log可以在事務提交之後直接刪除,不需要進行purge操作。

insert undo log格式

*表示對存儲的字段進行了壓縮。

  • 前兩個字節next記錄的是下一個undo log的位置
  • 尾部的兩個字節記錄的是undo的開始位置
  • type_cmpl佔用一個字節,記錄的是undo的類型,對於insert undo log該值是11.
  • undo_no記錄事務的ID
  • table_id記錄undo log所對應的表對象
  • 接着記錄所有主鍵的列和值,在rollback的時候,通過這些值定位到具體的記錄進行刪除即可。

在這裏插入圖片描述

update undo log

update undo log記錄的是對delete和update操作產生的undo log。該undo log可能需要提供MVCC機制,因此不能在事務提交的時候就刪除。提交時放入undo log鏈表,等待purge線程進行最後的刪除。
在這裏插入圖片描述
next、start、undo_no、table_id和之前介紹的insert undo log部分相同。
type_cmpl有5種,可能的值如下:

  • 12 TRX_UNDO_UPD_EXIST_REC更新non-delete-mark的記錄
  • 13 TRX_UNDO_UPD_DEL_REC將delete的記錄標記爲not delete。
  • 14 TRX_UNDO_DEL_NARK_REC將記錄標記爲DELETE。

update vector表示因爲update操作導致發生改變的列。每個修改的列信息都要記錄到undo log中,對於不同的undo log類型,可能還需要記錄對索引列所做的修改。

4、查看undo信息

InnoDB對information_schema進行了擴展,添加了兩張數據字典表,這樣用戶可以方便和快捷查看undo信息。

首先增加的數據字典表爲INNODB_TRX_ROLLBACK_SEGMENT。這個數據字典用來查看rollback segment。
在這裏插入圖片描述
可以通過如下的命令來查看rollback segment所在的頁:
在這裏插入圖片描述另外一個數據字典表爲INNODB_TRX_UNDO,用來記錄事務對應的undo log,方便DBA和開發人員詳細瞭解每個事務產生的undo量。

在這裏插入圖片描述

從上面可以看出:

  • 事務ID爲3001
  • rollback segment的ID爲2
  • 因爲這是事務的第一個操作,所以undo_rec_no爲0
  • 類型爲11,表示是insert undo log
  • size是undo log的大小,佔用12字節
  • 最後的space、page_no、offset表示undo log開始的位置,打開文件ibdata1,定位到頁(334,272),並讀取12字節內容
    在這裏插入圖片描述

觀察delete操作產生的undo log:
在這裏插入圖片描述
在這裏插入圖片描述

3、purge

delete和update操作可能並不直接刪除原有的數據。
比如下面的SQL:

  • delete from t where a=1;

表t上列a有聚集索引,對於上面的delete操作,通過前面關於undo log的介紹已經知道僅僅是將主鍵列等於1的記錄delete flag設置爲1,記錄並沒有被刪除,即記錄還是存在於B+樹中。

purge用於最終完成delete和update操作,這樣設計是因爲InnoDB存儲引擎支持MVCC,所以記錄不能在事務提交的時候立即進行處理,這時其他事務可能正在使用這行,故InnoDB存儲引擎需要保留記錄之前的版本。如果該行沒有其他事務引用,就可以進行真正的delete操作。

InnoDB存儲引擎還有一個history列表,它根據事務提交的順序,將undo log進行鏈接。

4、group commit

如果事務爲非只讀事務,每次事務提交的時候需要進行一次fsync操作,以此保證重做日誌都已經寫入了磁盤。當數據庫發生宕機的時候,可以通過重做日誌進行恢復。雖然固態硬盤的出現提高了磁盤的性能,然而磁盤的fsync性能是有限的,爲了提高fsync的效率,當前數據庫都提供了group commit的功能,即一次fsync可以刷新確保多個事務日誌被寫入文件,對於InnoDB存儲引擎來說,事務提交的時候會進行兩個階段的操作:

  • 修改內存中事務對應的信息,並且將日誌寫入重做日誌緩衝
  • 調用fsync將確保日誌都從重做日誌緩衝寫入磁盤

步驟2比步驟1慢,因爲步驟2要與磁盤打交道,但是當有事務進行這個過程的時候,其他事務可以進行步驟1的操作。進行步驟2的時候,可以將多個事務的重做日誌通過一次fsync刷新到磁盤,這樣就大大減少了磁盤的壓力。

在InnoDB1.2版本之前,在開啓二進制日誌後,InnoDB存儲引擎的group commit功能就會失效,從而導致性能的下降,因爲要使用主從複製,所以二進制日誌一般是開啓的狀態。

導致這個問題的原因是在二進制日誌開啓後,爲了保證存儲引擎中的事務和二進制日誌的一致性,二者之間使用了兩階段事務,其步驟如下:

  • 1)當事務提交的時候InnoDB存儲引擎進行prepare操作·
  • 2)MySQL數據庫上層寫入二進制日誌
  • 3)InnoDB存儲引擎將日誌寫入重做日誌文件
    1、修改內存中事務對應的信息,並且將日誌寫入重做日誌緩衝
    2、調用fsync將確保日誌都從重做日誌緩衝中寫入磁盤。

一旦步驟2)中的操作完成,就確保了事務的提交,即使在執行步驟3)的時候數據庫發生了宕機。需要注意的是每個步驟需要進行一次fsync才能保證上下兩層數據的一致性。

  • 步驟2)的fsync由參數sync_binlog控制
  • 步驟3)的fsync由參數innodb_flush_log_at_trx_commit控制

在這裏插入圖片描述
爲了保證二進制日誌寫入和InnoDB事務提交的順序一致,MySQL數據庫內部使用了prepare_commit_mutex這個鎖。但是在啓動這個鎖之後,步驟3)中的步驟1不可以在其他事務執行步驟2的時候進行,從而導致了group commit失效。
在這裏插入圖片描述
MySQL5.6通過BLGC(Binary Log Group Commit)實現了不但數據庫上層的二進制日誌寫入是group commit的,InnoDB存儲引擎層也是group commit的,此外還移除了原先的鎖prepare_commit_mutex,大大提高了數據庫的整體性。

在這裏插入圖片描述
MySQL上層提交時首先按照順序將其放入隊列,隊列的第一個事務叫做leader、其他事務稱爲follower。leader控制follower行爲,BLGC步驟分爲一下三階段:

  • Flush階段,將每個事務的二進制日誌寫入內存中
  • Sync階段,將內存中的二進制日誌刷新到磁盤,若隊列中有多個事務,僅僅一次fsync就完成了二進制日誌的寫入,這就是BLGC。
  • Commit階段:==Leader根據順序調用存儲引擎層事務的提交。==InnoDB存儲引擎就支持group commit。

參數binlog_max_flush_queue_time用來控制flush階段中等待的時間。

三、事務控制語句

顯式開啓事務命令:

  • begin
  • start transaction
  • set autocommit=0禁用當前會話的自動提交

一些事務控制語句

  • commit也可以寫成commit work,兩者幾乎是等價的。
  • rollback或者rollback work,兩者幾乎是等價的。
  • savepoint identifier:允許事務中創建一個保存點,一個事務可以有多個保存點。
  • release savepoint identifier:刪除事務的保存點。當沒有一個保存點執行這語句的時候,會拋出一次
  • rollback back to [savepoint] identifier:和savepoint一起使用,可以把事務回滾到之前的保存點。
  • set transaction:設置隔離級別

在存儲過程中,MySQL數據庫的分析器會自動將begin識別爲begin…end,因此在存儲過程中只能使用start transaction語句來開啓一個事務

commit work用來控制事務結束後的行爲是chain還是release的,如果是chain方式,那麼事務就變成了鏈事務。

==用戶可以通過參數completion_type來進行控制,

  • 默認爲0
  • 當爲1的時候,commit work相當於commit and chain,表示馬上自動開啓一個相同隔離級別的事務。
  • 當爲2時,commit work等同於commit and release,在事務提交之後就自動斷開與服務器的連接

注意點

rollback to savepoint,雖然有rollback,但其並不是真正的結束了一個事務,因此即使執行了rollback to savepoint,之後也需要顯式的運行commit或rollback命令。

四、隱式提交的SQL語句

以下的SQL會產生隱式的提交操作,即完成這些語句後,會有一個隱式的COMMIT操作:

  • DDL語句
  • 用來隱式修改MySQL架構的操作:
  • 管理語句

在這裏插入圖片描述

五、對於事務操作的統計

因爲InnoDB支持事務,因此需要在考慮每秒請求數的同時,關注每秒事務處理的能力(Transaction Per Second,TPS)。

計算TPS的方法是(com_commit + com_rollback)/time。但是利用這些方法進行計算的前提是:所有的事務必須是顯式進行提交的,如果存在隱式提交和回滾,不會計算到com_commit和com_rollback變量中。

在這裏插入圖片描述
在這裏插入圖片描述

六、事務的隔離級別

SQL保準定義的四個隔離級別爲:

  • READ UNCOMMITTED
  • READ COMMITTED
  • REPEATABLE READ
  • SERIALIZABLE

READ UNCOMMITTED稱爲瀏覽訪問、READ COMMITTED稱爲遊標穩定、REPEATABLE READ是2.9999度的隔離,沒有幻讀的保護,SERIALIZABLE稱爲隔離或3度的隔離。

InnoDB默認支持的存儲引擎隔離級別是REPEATABLE READ。
InnoDB存儲引擎在REPEATABLE READ事務隔離級別下,使用Next-Key Lock的算法,因此避免幻讀的產生。

隔離級別越低,事務請求的鎖越少或保持鎖的時間就越短。這也是爲什麼大多數數據庫默認的隔離級別是READ COMMITTED。

設置當前會話或全局事務的事務隔離級別命令:
在這裏插入圖片描述
如果想在MySQL啓動的時候就設置事務隔離級別,那就需要修改MySQL的配置文件,在mysqld中如下行:
在這裏插入圖片描述
查看當前會話的事務隔離級別:
在這裏插入圖片描述
查看全局的事務隔離級別,可以使用:
在這裏插入圖片描述

在SERIALIABLE的事務隔離級別,InnoDB存儲引擎會對每個SELECT語句後自動加上LOCK IN SHARE MODE,即爲每個讀取操作加上一個共享鎖。因此在這個事務隔離級別下,讀佔用了鎖。對一致性的非鎖定讀不再予以支持。

因爲InnoDB存儲引擎在REPEATABLE READ隔離級別下加上Next-Key Lock達到3度的效果,因此一般不再本地事務中使用SERIALIABLE的隔離級別,SERIALIABLE的事務隔離級別主要用於InnoDB存儲引擎的分佈式事務。

==在READ COMMITTED的事務隔離級別下,除了唯一性的約束檢查即外鍵約束的檢查需要gap lock,InnoDB存儲引擎不會使用Gap Lock的鎖算法。==在MySQL5.1中,READ COMMITTED事務隔離級別下默認只能在replication(複製)二進制日誌爲ROW的格式下,如果二進制日誌工作在默認的STATEMENT下,則會出現以下的錯誤。
在這裏插入圖片描述
在這裏插入圖片描述

例子

在master會話A開啓一個事務,不提交
在這裏插入圖片描述
在master上開啓另外一個會話B,執行如下事務:
在這裏插入圖片描述
接着會話A提交,查看錶A中的數據:
在這裏插入圖片描述
會看到數據產生了不一致,導致這個問題的原因有兩點:

  • 在READ COMMITTED事務隔離級別下,事務沒有使用gap lock進行鎖定,因此用戶在會話B中可以在小於等於5的範圍內插入一條記錄
  • STATEMENT格式記錄的是master上產生的SQL語句,因此master服務器上執行的順序爲先刪後插,但是在statement格式中記錄的卻是先插後刪,邏輯順序上產生了不一致。

READ REPEATABLE的事務隔離級別可以避免第一種情況的發生,也就避免了master slave數據不一致的問題。

七、分佈式事務

1、MySQL數據庫分佈式事務

InnoDB存儲引擎提供了對XA事務的支持,並通過XA事務支持分佈式事務的實現。
分佈式事務指的是運行多個獨立的事務資源參與到一個全局的事務中。事務資源通常是關係型數據庫系統,但是也可以是其他類型的資源。

全局事務要求在其他的所有參與的事務要麼都提交,要麼都回滾。

在使用分佈式事務時,InnoDB存儲引擎的事務隔離級別必須設置爲SERIALIZABLE。

XA事務允許不同數據庫之間的分佈式事務,只要參與在全局事務中的每個節點都支持XA事務。

例子:
在這裏插入圖片描述
在這樣的情況下一定需要使用分佈式事務來保證數據的安全。

XA事務由一個或者多個資源管理器、一個事務管理器以及一個應用程序組成。

  • 資源管理器:提供訪問事務資源的方法。通常一個數據庫就是一個資源管理器。
  • 事務管理器:協調參與全局事務中的各個事務。需要和參與全局事務的所有資源管理器進行通信。
  • 應用程序:定義事務的邊界,指定全局事務中的操作。

在這裏插入圖片描述

分佈式事務使用兩段式提交的方式:

  • 第一階段:所有參與全局事務的節點都開始準備,告訴事務管理器他們已經準備好提交了
  • 第二階段:事務管理器告訴資源管理器執行rollback還是commit。如果任何一個節點顯示不能提交,則所有的節點都被告知需要回滾。

和本地事務不同的是,分佈式事務需要多一次的prepare操作,待收到所有節點的同意信息之後,再進行commit或者rollback操作。

MySQL數據庫XA事務的SQL語法如下:
在這裏插入圖片描述
在這裏插入圖片描述

在單個節點上運行XA事務的例子:
在這裏插入圖片描述
通常來說,都是通過編程語言來完成分佈式事務的操作,當前的Java的JTA(Java Transaction API)都可以很好的支持MySQL的分佈式事務。

使用JTA實現分佈式事務的例子:P338

2、內部XA事務

之前討論的分佈式事務是外部事務,即資源管理器是MySQL數據庫本身,在MySQL數據庫中還存在另外一種分佈式事務,其在存儲引擎和插件之間,又或者在存儲引擎和存儲引擎之間,稱之爲內部XA事務。

最爲常見的XA事務存在與binlog和InnoDB存儲引擎之間,由於複製的需要,很多數據庫都開啓了binlog功能。在提交的時候,先寫二進制日誌,再寫InnoDB的重做日誌。這兩個操作必須是原子的,不然可能存在主從不一致的情況。

爲了解決這個問題,MySQL在binlog和InnoDB存儲引擎之間採用XA事務,在事務提交的時候,InnoDB存儲引擎會先做一個PREPARE操作,將事務的xid寫入,接着進行二進制日誌的寫入。
在這裏插入圖片描述

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