mysql技術內幕(七)事務

事務

事務會把數據庫從一種一致狀態轉換爲另一種一致狀態。在數據庫提交工作時,可以確保要麼所有修改都已經保存了,要麼所有修改都不保存。InnoDB 存儲引擎中的事務完全符合ACID 的特性。ACID 是以下4 個詞的縮寫:

  • 原子性(atomicity)
  • 一致性(consistency)
  • 隔離性(isolation)
  • 持久性(durability )

7.1 認識事務

7.1.1 概述

A (Atomicity), 原子性。指整個數據庫事務是不可分割的工作單位。只有使事務中所有的數據庫操作都執行成功,纔算整個事務成功。
C (consistency), 一致性。一致性指事務將數據庫從一種狀態轉變爲下一種一致的狀態。在事務開始之前和事務結束以後,數據庫的完整性約束沒有被破壞。
I (isolation) , 隔離性。隔離性還有其他的稱呼,如併發控制(concurrency control) 、可串行化(serializability) 、鎖(locking) 等。事務的隔離性要求每個讀寫事務的對象對其他事務的操作對象能相互分離,即該事務提交前對其他事務都不可見,通常這使用鎖來實現。
D (durability), 持久性。事務一旦提交,其結果就是永久性的。即使發生右機等故
障,數據庫也能將數據恢復

7.1.2 分類

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

  • 扁平事務(Flat Transactions)
  • 帶有保存點的扁平事務
  • 鏈事務
  • 嵌套事務( Nested Transactions)
  • 分佈式事務( Distributed Transactions)

  扁平事務(Flat Transaction) 是事務類型中最簡單的一種,但在實際生產環境中,這可能是使用最爲頻繁的事務。在扁平事務中,所有操作都處於同一層次,其由BEGIN WORK 開始, 由COMMIT WORK 或ROLLBACK WORK 結束,其間的操作是原子的,要麼都執行,要麼都回滾。
   帶有保存點的扁平事務(Flat Transactions with Savepoint), 除了支持扁平事務支持的操作外,允許在事務執行過程中回滾到同一事務中較早的一個狀態。這是因爲某些事務可能在執行過程中出現的錯誤並不會導致所有的操作都無效,放棄整個事務不合乎要求,開銷也太大。保存點(Savepoint) 用來通知系統應該記住事務當前的狀態,以便當之後發生錯誤時,事務能回到保存點當時的狀態
   鏈事務(Chained Transaction) 可視爲保存點模式的一種變種。帶有保存點的扁平事務,當發生系統崩潰時,所有的保存點都將消失,因爲其保存點是易失的(volatile),而非持久的(persistent) 。這意味着當進行恢復時,事務需要從開始處重新執行,而不能從最近的一個保存點繼續執行。
  鏈事務的思想是:在提交一個事務時,釋放不需要的數據對象,將必要的處理上下文隱式地傳給下一個要開始的事務。注意,提交事務操作和開始下一個事務操作將合併爲一個原子操作。這意味着下一個事務將看到上一個事務的結果,就好像在一個事務中進行的一樣。
  鏈事務與帶有保存點的扁平事務不同的是,帶有保存點的扁平事務能回滾到任意正確的保存點。而鏈事務中的回滾僅限於當前事務,即只能恢復到最近一個的保存點。對於鎖的處理,兩者也不相同。鏈事務在執行COMMIT 後即釋放了當前事務所持有的鎖,而帶有保存點的扁平事務不影響迄今爲止所持有的鎖。
  嵌套事務(Nested Transaction) 是一個層次結構框架。由一個頂層事務(toplevel transaction) 控制着各個層次的事務。頂層事務之下嵌套的事務被稱爲子事務(subtransaction), 其控制每一個局部的變換。
  分佈式事務(Distributed Transactions) 通常是一個在分佈式環境下運行的扁平事務, 因此需要根據數據所在位置訪問網絡中的不同節點。

7.2 事務的實現

7.2.1 redo

1. 基本概念
  重做日誌用來實現事務的持久性,即事務ACID 中的D 。其由兩部分組成: 一是內存中的重做日誌緩衝(redo log buffer), 其是易失的;二是重做日誌文件(redo log file),其是持久的。
  InnoDB 是事務的存儲引擎,其通過Force Log at Commit 機制實現事務的持久性,即當事務提交(COMMIT) 時,必須先將該事務的所有日誌寫入到重做日誌文件進行持久化,待事務的COMMIT 操作完成纔算完成。這裏的日誌是指重做日誌,在InnoDB 存儲引擎中,由兩部分組成,即redo log 和undo log 。redo log 用來保證事務的持久性, undo log 用來幫助事務回滾及MVCC 的功能。redo log 基本上都是順序寫的,在數據庫運行時不需要對redo log 的文件進行讀取操作。而undo log 是需要進行隨機讀寫的。

7.2 .2 undo

1. 基本概念
  重做日誌記錄了事務的行爲,可以很好地通過其對頁進行“重做”操作。但是事務有時還需要進行回滾操作,這時就需要undo 。因此在對數據庫進行修改時, InnoDB 存儲引擎不但會產生redo, 還會產生一定世的undo。這樣如果用戶執行的事務或語句由於某種原因失敗了,又或者用戶用一條ROLLBACK 語句請求回滾,就可以利用這些undo信息將數據回滾到修改之前的樣子。
  redo 存放在重做日誌文件中,與redo 不同, undo 存放在數據庫內部的一個特殊段
(segment) 中,這個段稱爲undo 段(undo segment) 。undo 段位千共享表空間內。

7.2.3 purge

   delete 和update 操作可能並不直接刪除原有的數據。
   undo log 的介紹已經知道僅是將主鍵列等於1 的記錄delete flag 設置爲1. 記錄並沒有被刪除,即記錄還是存在於B+ 樹中。其次,對輔助索引上a 等於1 , b 等於l 的記錄同樣沒有做任何處理,甚至沒有產生undo log。而真正刪除這行記錄的操作其實被"延時"
了,最終在purge 操作中完成。
   purge 用於最終完成delete 和update 操作。這樣設計是因爲InnoDB 存儲引擎支持MVCC ,所以記錄不能在事務提交時立即進行處理。這時其他事物可能正在引用這行,故InnoDB 存儲引擎需要保存記錄之前的版本。而是否可以刪除該條記錄通過purge來進行判斷。若該行記錄已不被任何其他事務引用,那麼就可以進行真正的delete 操作。可見, purge操作是清理之前的delete 和update 操作,將上述操作"最終"完成。而實際執行的操作爲delete 操作, 清理之前行記錄的版本。

7.2.4 group commit

  若事務爲非只讀事務,則每次事務提交時需要進行一次fsync 操作,以此保證重做日誌都已經寫人磁盤。當數據庫發生者機時,可以通過重做日誌進行恢復。雖然固態硬盤的出現提高了磁盤的性能,然而磁盤的fsync 性能是有限的。爲了提高磁盤fsync 的效率,當前數據庫都提供了group commit 的功能,即一次fsync 可以刷新確保多個事務日誌被寫人文件。對於InnoDB 存儲引擎來說,事務提交時會進行兩個階段的操作:
1 )修改內存中事務對應的信息,並且將日誌寫人重做日誌綏衝。
2) 調用fsync 將確保日誌都從重做日誌緩衝寫人磁盤。
  步驟2) 相對步驟1 )是一個較慢的過程,這是因爲存儲引擎需要與磁盤打交道。但當有事務進行這個過程時,其他事務可以進行步驟1 )的操作,正在提交的事物完成提交操作後,再次進行步驟2) 時,可以將多個事務的重做日誌通過一次fsync 刷新到磁盤,這樣就大大地減少了磁盤的壓力,從而提高了數據庫的整體性能。對於寫人或更新較爲頻繁的操作, group commit 的效果尤爲明顯。

7.3 事務控制語句

   在MySQL 命令行的默認設置下,事務都是自動提交( auto commit ) 的,即執行SQL 語句後就會馬上執行COMMIT 操作。兇此要顯式地開啓一個事務鏘使用命令BEGIN 、START TRANSACTION ,或者執行命令SET AUTOCOMMIT啡, 禁用當前會話的自動提交。每個數據庫廠商自動提交的設置都不相同,每個DBA 或開發人員需要非常明白這一點, 這對之後的SQL 編程會有非凡的意義,因此用戶不能以之前的經驗來判斷MySQL 數據庫的運行方式。在具體介紹其含義之前, 先來看看用戶可以使用哪些事務控制語句。

  • STARTTRANSACTION I BEGIN: 顯式地開啓一個事務。
  • COMMIT: 要想使用這個語句的最簡形式, 只箭發出COMMIT o 也可以更詳細一些, 寫爲COMMIT WORK , 不過這二者幾乎是等價的。COMMIT 會提交事務,並使得已對數據庫做的所有修改成爲永久性的。
  • ROLLBACK : 要想使用這個語句的最簡形式, 只需發出ROLLBACK o 同樣地,也可以寫爲ROLLBACK WORK ,但是二者兒乎是等價的。回滾會結束用戶的事務, 並撤銷正在進行的所有未提交的修改。
  • SAVEPOINT identifier : SAVEPOINT 允許在事務中創建一個保存點, 一個事務中可以有多個SAVEPOINT 。
  • RELEASE SAVEPOINT identifier : 刪除一個事務的保存點, 當沒有一個保存點執行這句語句時,會拋出一個異常。
  • ROLLBACK TO[SAVEPOINT] identifier: 這個語句與SAVEPOINT 命令一起使用。可以把事務回滾到標記點,而不回滾在此標記點之前的任何工作。例如可以發出兩條UPDATE 語句,後面跟一個SAVEPOINT ,然後義是兩條DELETE 語句。如果執行DELETE 語句期間出現了某種異常情況,並且捕獲到這個異常,同時發出了ROLLBACK TO SAVEPOINT 命令,事務就會回滾到指定的SAVEPOINT ,撤銷DELETE 完成的所有工作, 而UPDATE 語句完成的工作不受影響。
  • SET TRANSACTION :這個語句用來設置事務的隔離級別。InnoDB 存儲引擎在提供的事務隔離級別有: READ UNCOMMITTED 、READ COMMITTED 、REPEATABLE READ 、SERIALIZABLE 。
      START TRANSACTION、BEGIN 語句都可以在時QL 命令行下顯式的開啓一個事務。但是在存儲過程中, MyS QL 數據庫的分析器會自、動將BEGIN 識別爲B EG IN …END , 因此在存儲過程中只能使用START TRANSA CTION 語句來開啓一個事務。
      COMMIT 和COMMIT WORK 語句基本是一致的, 都是用來提交事務。不同之處在於COMMITWORK 用來控制事務結束後的行爲是CHAIN 還是RELEASE 的。如果是SHAIN方式,那麼事務就變成了鏈事務。

7.4 隱式提交的SQL 語句

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

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

7.5 對於事務操作的統計

  由於lnnoDB 存儲引擎是支持事務的,因此InnoDB 存儲引擎的應用需要在考慮每秒請求數( Question Per Second, Q PS ) 的同時,應該關注每秒事務處理的能力( Transaction Per Second, TPS ) 。
  計算TPS 的方法是(com commit+com rollback) /time. 但是利用這種方法進行計算的前提是:所有的事務必須都是顯式提交的,如果存在隱式地提交和回滾(默認autocomrnit= 1 ) ,不會計算到com comrnit 和com rollback 變量中。

7.6 事務的隔離級別

ISO 和ANIS SQL 標準制定了四種事務隔離級別的標準。
SQL 標準定義的四個隔離級別爲:

  • READ UNCOMMITTED
  • READ COMMITTED
  • REPEATABLE READ
  • SERIALIZABLE

7.7 分佈式事務

7.7.1 MySQL 敢據庫分佈式事務
  InnoDB 存儲引擎提供了對XA 事務的支持,並通過XA 事務來支持分佈式事務的實現。分佈式事務指的是允許多個獨立的事務資源(transactional resources) 參與到一個全局的事務中。事務資源通常是關係型數據庫系統,但也可以是其他類型的資源。全局事務要求在其中的所有參與的事務要麼都提交,要麼都回滾,這對於事務原有的ACID 要求又有了提高。另外,在使用分佈式事務時, InnoDB 存儲引擎的事務隔離級別必須設置爲SERIALIZABLE 。
  XA 事務允許不同數據庫之間的分佈式事務,如一臺服務器是MySQL 數據庫的,另一臺是Oracle 數據庫的,又可能還有一臺服務器是SQL Server 數據庫的,只要參與在全局事務中的每個節點都支持XA 事務。分佈式事務可能在銀行系統的轉賬中比較常見。

  XA 事務由一個或多個資源管理器(Resource Managers)、一個事務管理器(Transaction Manager) 以及一個應用程序(Application Program) 組成。

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

  分佈式事務使用兩段式提交(two-phase commit) 的方式。在第一階段,所有參與全局事務的節點都開始準備(PREPARE) ,告訴事務管理器它們準備好提交了。在第二階段,事務管理器告訴資源管理器執行ROLLBACK 還是COMMIT。如果任何一個節點顯示不能提交,則所有的節點都被告知需要回滾。可見與本地事務不同的是,分佈式事務需要多一次的PREPARE 操作,待收到所有節點的同意信息後,再進行COMMIT 或是ROLLBACK 操作。

7.7.2 內部XA 事務

  在MySQL 數據庫中還存在另外一種分佈式事務,其在存儲引擎與插件之間,又或者在存儲引擎與存儲引擎之間,稱之爲內部XA 事務。
  最爲常見的內部XA 事務存在於binlog 與lnnoDB 存儲引擎之間。由於複製的需要,因此目前絕大多數的數據庫都開啓了binlog 功能。在事務提交時,先寫二進制日誌,再寫lnnoDB 存儲引擎的重做日誌。對上述兩個操作的要求也是原子的,即二進制日誌和重做日誌必須同時寫入。若二進制日誌先寫了,而在寫人lnnoDB 存儲引擎時發生了宕機,那麼slave 可能會接收到 master 傳過去的二進制日誌並執行,最終導致了主從不一致的情況。

7.8 不好的事務習慣

1、在循環中提交
2、使用自動提交
3、使用自動回滾

7.9 長事務

  通過批量處理小事務來完成大事務的邏輯。每完成一個小事務,將完成品的結果存放在batchcontext 表中, 表示已完成批量事務的最大賬號ID 。若事務在運行過程中產生問題,需要重做事務,可以從這個已完成的最大事務ID 繼續進行批量的小事務,這樣重新開啓事務的代價就顯得比較低,也更容易讓用戶接受。batchcontext 表的另外一個好處是,在長事務的執行過程中,用戶可以知道現在大概已經執行到了哪個階段。比如一共有1億條的記錄,現在表batchcontext 中最大的賬號ID 爲4000 萬,也就是說這個大事務大
概完成了40% 的工作。
  這裏還有一個小地方需要注意,在從表account 中取得max account no 時,人爲地加上了一個共享鎖,以保證在事務的處理過程中,沒有其他的事務可以來更新表中的數據,這是有意義的,並且也是非常有必要的操作。

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