InnoDB 存儲引擎的事務學習

本文主要是針對《Mysql技術內幕:InnoDB 存儲引擎》一書中第七章關於 InnoDB 存儲引擎中事務的學習總結。mysql

事務是訪問並更新數據庫各類數據項的一個程序執行單元,是數據庫區別於文件系統的重要特性之一。算法

事務的 ACID 特性

理論上,事務必須知足 ACID 特性,纔算嚴格的定義,其中對於 InnoDB 存儲引擎來講,其默認事務隔離級別爲 READ REPEATABLE,徹底遵循和知足事務的 ACID 特性。sql

  • A(Atomicity) 原子性
一個事務必須被視爲一個不可分割的最小工做單元,要麼所有提交成功,要不所有失敗回滾
複製代碼
  • C(Consistency) 一致性
一致性是指數據庫從一個執行性狀態轉換爲另一個一致性狀態
事務的一致性是但願全部操做是符合現實當中的指望的,好比事務的結束後,完整性約束有沒有被破壞,是否發生丟失更新,髒讀,不可重複讀等問題
因此事務的一致性是一個綜合的概念,要靠事務的原子性,隔離性和持久性來保證
複製代碼
  • I(Isolation) 隔離性
隔離性是指事務所作的修改在最終提交前,對其它事務是不可見的,一般是靠鎖機制來實現的
複製代碼
  • D(Durability) 持久性
事務一旦提交,,其所作的修改是永久保存在數據庫裏的,即便系統奔潰數據頁不會丟失
持久性保證事務系統的高可靠性,而高可用性事務自己沒法保證,須要一些系統共同配合完成
複製代碼

事務的分類

事務分爲扁平事務,帶有保存點的扁平事務,鏈事務,嵌套事務,分佈式事務。對於 InnoDB 存儲引擎來講,其支持扁平事務,帶保存點的事務,鏈事務,分佈式事務。對於嵌套事務,其原生不支持數據庫

  • 扁平事務
- 扁平事務由 BEGIN WORK 開始,ROLLBACK 或者 COMMIT 結束,其間是原子的,要麼執行,要麼回滾
複製代碼
  • 帶有保存點的扁平事務
- 除了支持扁平事務支持的操做外,容許在事務執行過程當中回滾同一事務中較早的一個狀態
- 由於某些事務可能在執行過程當中出現的錯誤並不會致使全部的操做都無效,放棄整個事務不合乎要求,開銷太大
- 保存點用來通知事務系統應該記住事務當前的狀態,以便當以後發生錯誤時,事務能回到保存點當時的狀態
複製代碼
  • 鏈事務
- 鏈事務的思想是:在提交一個事務時,釋放不須要的數據對象,將必要的處理上下文隱式地傳給下一個要開始的事務
- 提交事務操做和開始下一個事務操做 將合併爲一個原子操做,這意味着下一個事務將看到上一個事務的結果,就好像一個事務中進行的同樣
- 鏈事務與帶有保存點的扁平事務不一樣的是,帶有保存點的扁平事務能回滾到任意正確的保存點,而鏈事務中的回滾僅限當前事務,即只能恢復到最近的一個保存點
- 對於鎖的處理,二者也不相同,鎖事務在執行 COMMIT 後即釋放了當前所持有的鎖,而帶有保存點的扁平事務不影響迄今爲止所持有的鎖
複製代碼
  • 嵌套事務
- 嵌套事務是一個層次結構框架,由一個頂層事務控制着各個層次的事務,頂層事務之下嵌套的事務被稱爲子事務
- 嵌套事務是由若干事務組成的一棵樹,子樹既能夠是嵌套事務也能夠是扁平事務
- 處在葉節點的事務是扁平事務,可是每一個事務從根到葉節點的距離能夠是不一樣的
- 子事務既能夠提交也能夠回滾。可是它的提交操做並不立刻生效。除非其父事務已經提交。所以能夠推論出,任何子事務都在頂層事務提交後才真正的提交
- 樹中的任意事務回滾會引發它的全部子事務一同回滾,故子事務僅保留 ACI 特性而不具備 D 特性
- 實際的工做是交由葉子節點完成,即只有葉子節點的事務才能才能訪問數據庫、發送信息、獲取其餘類型的資源,而高層的事務僅負責邏輯控制
複製代碼
  • 分佈式事務
- 分佈式事務一般是一個分佈式環境下運行的扁平事務,所以須要根據數據所在位置訪問網絡中的不一樣節點
- 分佈式事務通常不能經過一臺數據庫就完成任務,其須要訪問網絡中兩個節點或者多個的數據庫,而在每一個節點的數據庫執行的實務操做有都是扁平的
- 對於分佈式事務,其一樣須要知足ACID特性,要麼都發生,要麼都失敗
複製代碼

事務的實現

事務的隔離性是經過鎖來實現的,而原子性,一致性,持久性是經過數據庫中的 redo log 和 undo log 來實現的。緩存

redo log
  • 重作日誌由兩部分組成:內存中中的重作日誌緩存和磁盤上的重作日誌文件
  • InnoDB 存儲引擎的重作日誌是在事務進行中不斷的寫入,並非在事務提交時才寫入,先寫入重作日誌緩存,而後根據必定規則刷新到重作日誌文件
  • 重作日誌文件記錄了 InnoDB 存儲引擎的事務日誌,主要用來在發生宕機的狀況下,對事務操做進行恢復操做,歷來保證的原子性和持久性
  • 重作日誌緩存:
- 重作日誌緩存不放在緩存池中,是另一份單獨的緩存
- 重作日誌緩存不須要特別大,由於 Master Thread 每秒都會將數據刷新到磁盤
- 經過參數 innodb_log_buffer_size 來修改重作日誌緩存的大小,默認爲 8MB
- 重作日誌緩存都會被刷新到磁盤的三種條件:1. Master Thread 會每秒進行刷新 2. 每一個事務提交時會刷新 3.重作日誌緩存空間小於 1/2 時會強制刷新
複製代碼
  • 重作日誌文件:
- 重作日誌文件至少有一個重作日誌文件組,每一個重作日誌文件組至少有 2 個重作日誌文件
- 爲了保證高可用性,能夠設置鏡像日誌文件組,將日誌組存儲在不一樣的磁盤上
- 重作日誌的寫入不須要 doublewrite,由於寫入是按照一個扇區的大小進行寫入的,是寫入的最小單位,因此能夠保證寫入的可靠性
複製代碼
undo log
  • undo log 主要用於用戶在執行 rollback 語句時進行回滾操做
  • redo 存放在日誌文件裏,而 undo存放在數據庫共享表空間的一個是特殊段中(undo segment)
  • undo 是邏輯日誌,在回滾時全部修改邏輯的被取消了,可是數據結構和頁自己在回滾後可能大不相同
  • undo 其實就是作與先前相反的操做,對於每一個 Insert 執行 delete,對每個 update 執行相反的 update
  • undo 另一個做用是實現 MVCC(多版本控制),實現非鎖定讀,經過 undo 日誌找到以前的行版本信息
  • uodo log 也會產生 redo log,由於 undo log 也須要持久性的保護
  • undo log 的回收和刪除
- 並非當事務提交時,undo log 就會被刪除,頗有可能還有其餘事務在使用該 undo log 來記錄以前的版本
- 因此事務提交時 undo log 會被放在待刪除列表裏,以後經過 purge 線程來根據是否有其它事務在使用來決定刪除
- 對於 insert 操做的記錄,只對本事務可見,因此在事務提交後能夠直接刪除,不須要進行 purge 操做
- 經過全局參數 innodb_purge_batch_size 來設置每次 purge 操做能夠清理的 undo 頁的數量
複製代碼

事務控制語句

  • Mysql 命令行的默認設置下,事務是自動提交的,能夠經過執行命令 SET AUTOCOMMIT = 0 來禁止自動提交
  • 關於事務的控制語句總結以下:
- START TRANSACTION | BEGIN:顯式地開啓一個事務
- COMMIT:會提交事務,並使得已對數據庫作的全部修改爲爲永久性的
- COMMIT WORK:COMMIT WORK 和 COMMIT 語句基本是一致的,不一樣之處在於 COMMIT WORK 用來控制事務結束後的行爲是 CHAN 仍是 RELEASE 的
            用戶能夠經過參數 completion_type 來進行控制 COMMIT WORK 的行爲,該參數默認爲 0,在這種設置下 COMMIT和 COMMIT WORK是徹底等價的。
            當參數 completion_type的 值爲 1 時, COMMIT WORK 等同於 COMMIT AND CHAIN,表示立刻自動開啓個相同隔離級別的事務
- ROLLBACK:回滾並結束用戶的事務,並撤銷正在進行的全部未提交的修改
- ROLLBACK WORK:ROLLBACK WORK 和 ROLLBACK 的關係與 COMMIT WORK 和 COMMIT 關係相似
- SAVEPOINT identifier: SAVEPOINT 容許在事務中建立一個保存點,一個事務中能夠有多個 SAVEPOINT
- RELEASE SAVEPOINT identifier:刪除一個事務的保存點,當沒有一個保存點執行這句語句時,會拋出一個異常
- ROLLBACK TO [SAVEPOINT] identifier:這個語句與 SAVEPOINT 命令一塊兒使用,能夠把事務回滾到標記點,而不回滾在此標記點以前的任何工做
- SET TRANSACTION:這個語句用來設置事務的隔離級別。 InnoDB存儲引擎提供的事務隔離級別有: READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ、 SERIALIZABLE。

複製代碼
  • ROLLBACK TO SAVEPOINT 來回滾到某個保存點,可是若是回滾到一個不存在的保存點,會拋出異常
  • ROLLBACK TO SAVEPOINT 雖然有 ROLLBACK,但其並非真正地結束一個事務,所以即便執行了 ROLLBACK TO SAVEPOINT ,以後也須要顯式地運行 COMMIT 或 ROLLBACK 命令
  • 經過變量 com_commit 和 com_rollback 能夠查看每秒鐘事務的提交量和回滾量,這個統計是全部顯式提交的事務

事務的隔離級別

在文章 InnoDB 存儲引擎的鎖學習 中就屢次提到事務的隔離級別,不一樣粒度的鎖在不一樣隔離級別下的表現。隔離級別越低,數據庫所須要的鎖越少,性能越好,可是安全性越低。接下來是對事務的四種隔離級別(由低到高)的總結:安全

  • Read Uncommitted(讀取未提交內容)
- 在該隔離級別,全部事務均可以看到其餘未提交事務的執行結果
- 本隔離級別不多用於實際應用,由於它的性能也不比其餘級別好多少
- 讀取未提交的數據,也被稱之爲髒讀問題
- 該隔離級別違背了事務的隔離性原則
複製代碼
  • Read Committed(讀取提交內容)
- 一個事務只能看見已經提交事務所作的改變,知足隔離性的定義
- 該隔離級別下會出現同一個事務兩次讀同一份數據結果不一致現象,也就是不可重複讀問題
複製代碼
  • Repeatable Read(可重讀)
- 這 是MySQL 的默認事務隔離級別,它確保同一事務的多個實例在併發讀取數據時,會看到一樣的數據行
- Repeatable Read 這會致使幻讀 (Phantom Read)問題。幻讀指當用戶讀取某一範圍的數據行時,另外一個事務又在該範圍內插入了新行,當用戶再讀取該範圍的數據行時,會發現有新的「幻影」 行。
- InnoDB 利用 Gap Lock 算法在  Repeatable Read 隔離級別下解決了 幻讀現象
複製代碼
  • Serializable(可串行化)
- 這是最高的隔離級別,它經過強制事務排序,使之不可能相互衝突,從而解決幻讀問題。
- 它是在每一個讀的數據行上加上共享鎖,在這個級別,可能致使大量的超時現象和鎖競爭。
複製代碼

很差的事務習慣

  • 在循環中提交事務
- 當插入發生錯誤時,數據庫會停留在一個未知的位置,沒法回滾
- 每一次提交都要寫一次重作日誌,性能會變的不好
CREATE PROCEDURE load1(count int unsigned)
begin

  declare s int unsigned default 1;

  declare c char(80) default repeat('a',80);

  while s<=count do

    insert into t1 select NULL,c;

    commit;

    set s=s+1;

  end while;

end;
複製代碼
  • 使用自動提交事務
- 自動提交併非好習慣,會讓一些開發人員可能產生錯誤的理解
- 另外,在不一樣的語言API時,自動提交是不一樣的,用不一樣的語言來編寫數據庫應用程序前,應該對鏈接MySQL的API作好研究
- 在編寫應用程序開發時,最好把事務的控制權限交給開發人員,即在程序端進行事務的開始和結束
複製代碼
  • 使用自動回滾
- 自動回滾的狀況下,開發人員沒法定位發生了什麼樣的錯誤
- 對於事務的 BEGIN,COMMIT,ROLLBACK 操做應該交給程序端來控制,當發生錯誤時,咱們能夠把異常拋出去再進行回滾
複製代碼
相關文章
相關標籤/搜索