MySQL事务实现原理详解
1.事務概述
事務的詳細概述
什么是事務?
事務時是訪問和更新數據的程序執行單元,事務中可能含有一個或多個SQL語句,這些語句要么全部執行,要么都不執行
回顧MySQL的邏輯架構與存儲引擎
如上圖所示,MySQL服務器的邏輯架構從上到下分為三層:
- 第一層:處理客戶端連接、授權認證等
- 第二層:服務器層,負責查詢語句的解析、優化、緩存以及內置函數的實現、存儲過程等。
- 第三層:存儲引擎,負責MySQL中數據的存儲與提取。MySQL中服務器層不管理事務,事務是由存儲引擎實現的,MySQL支持事務的存儲引擎有InnoDB,NDB Cluster等,使用最多的就是InnoDB,也是MySQL默認使用的存儲引擎,其他引擎不支持事務,比如MyISAM,Memory等。
事務的四個特性
ACID是衡量事務的四個標準:
- 原子性(Atomicity)
- 一致性(Consistency)
- 隔離性(Isolation)
- 持久性(Durability)
按照嚴格的標準,只有同時滿足ACID特性才是事務.
2.A原子性實現原理
原子性定義
原子性指一個事務是一個不可分割的整體,內部所有的操作要么都做,要么都不做,如果事務中的一條SQL執行失敗,那么已經執行的語句也必須回滾.所以說,實現原子性的核心就在于如何實現回滾。
MySQL的日志
在說明原子性原理之前,首先介紹一下MySQL的事務日志,MySQL的日志有很多種,比如二進制日志(binlog)、錯誤日志、查詢日志、慢查詢日志,此外InnoDB存儲引擎還提供了兩種事務日志,redo log(重做日志) 和 undo log(回滾日志), 其中redo log用于保證事務的持久性,而undo log則是事務原子性和隔離性實現的基礎。
原子性實現關鍵:undo log
- 實現原子性的關鍵,是當事務回滾時能夠撤銷所有已經執行成功的SQL語句,InnoDB實現回滾,靠的是undo log,當事務對數據庫進行修改時,InnoDB會生成對應的;如果事務執行失敗或者調用了rollback,導致事務需要回滾,就利用undo log中的信息將數據回滾到修改之前的樣子
- undo log屬于邏輯日志,它記錄的是SQL執行相關的信息,當發生回滾時,InndDB會根據undo log的內容做與之前相反的工作,對于每個insert,回滾時會執行delete,對于每個update,回滾時會執行一個相反的update,將數據改回去。
- 以update操作為例,當事務執行update時,其生成的undo log中會包含被修改行的主鍵(一遍知道修改了哪些行),修改了哪些列、這些列在修改前后的值的信息,回滾時便可以使用這些信息將數據還原到update之前的狀態。
3.D持久性實現原理
持久性定義
持久性指事務一旦提交,它對數據庫的改變就應該是永久的,接下來的其他操作不應該對其有任何影響,并且不能回滾。
redo log存在的背景
redo log與undo log都屬于InnoDB的事務日志,下面聊一下redo log存在的背景
InnoDB作為MySQL的存儲引擎,數據時放在磁盤的,但是如果每次讀寫數據都需要磁盤IO,效率會很低,為此,InnoDB提供了緩存(Buffer Pool),BP中包含了部分數據頁的映射,作為訪問數據庫的緩沖;當從數據庫讀取數據時,會首先寫入BP,BP中修改的數據會定期刷新到磁盤中(這一過程稱為刷臟)
BP的使用大大提高了讀寫數據的效率,但是也帶來了新的問題,如果MySQL宕機,而此時BP中修改的數據還沒有刷新的磁盤,就會導致數據的丟失,事務的持久性無法保證。
持久性實現原理 redo log
redo log就被引入來解決這個問題(宕機導致BP中的數據沒有刷新磁盤,造成數據丟失),當數據被修改時,除了修改BP中的數據,還會在redo log中記錄這次操作,當事務提交時,會調用fsync接口對 redo log進行刷盤,如果MySQL宕機,重啟時可以讀取redo log中的數據,對數據庫進行恢復,redo log采用的是WAL(Write-ahead logging,預寫式日志), 所有修改先寫入日志,在更新到BP,保證了數據不會因為MySQL宕機而丟失,從而滿足了持久性的要求。
參考redo log 二段式提交
既然redo log也需要在事務提交是將日志寫入磁盤,為什么它比直接將BP中修改的數據寫入磁盤(即刷臟)要快呢?
redo log 與 binlog
我們知道,在MySQL中還存在 binlog(二進制日志) 也可以記錄寫操作并用于數據的恢復,但二者是有著根本的不同的:
(1)作用不同:redo log是用于crash recovery的,保證MySQL宕機也不會影響持久性;binlog是用于point-in-time recovery的,保證服務器可以基于時間點恢復數據,此外binlog還用于主從復制,binlog簡單描述binlog實現主從復制原理以及工作流程
(2)層次不同:redo log是InnoDB存儲引擎實現的,而binlog是MySQL的服務器層(可以參考文章前面對MySQL邏輯架構的介紹)實現的,同時支持InnoDB和其他存儲引擎。
(3)內容不同:redo log是物理日志,內容基于磁盤的Page;binlog的內容是二進制的,根據binlog_format參數的不同,可能基于sql語句、基于數據本身或者二者的混合。
(4)寫入時機不同:binlog在事務提交時寫入;redo log的寫入時機相對多元:
- 前面曾提到:當事務提交時會調用fsync對redo log進行刷盤;這是默認情況下的策略,修改innodb_flush_log_at_trx_commit參數可以改變該策略,但事務的持久性將無法保證。
- 除了事務提交時,還有其他刷盤時機:如master thread每秒刷盤一次redo log等,這樣的好處是不一定要等到commit時刷盤,commit速度大大加快。
4.I隔離性實現原理
隔離性定義
與原子性和持久性側重與研究事務本身不同,隔離性研究的是不同事務之間的相互影響,隔離性是指,事務內部的操作與其他事務是隔離的,并發執行的各個事務之間不能互相干擾,嚴格的隔離性對應了事務的隔離級別中的Serializable(可串行化),但實際應用中處于性能考慮很少會使用次級別。MySQL隔離級別與臟讀不可重復讀幻讀參考
隔離性追求的是并發情況下事務之間互不干擾,簡單起見,我們主要考慮最簡單的讀操作和寫操作(加鎖讀等特殊情況會特殊說明),那么隔離性的探討,主要可以分為兩個方面。
- (一個事務)的寫操作對(另一個事務)寫操作的影響:鎖機制保證隔離性
- (一個事務)的寫操作對(另一個事務)讀操作的影響: MVCC保證隔離性
鎖機制的原理簡單概述
鎖機制的基本原理可以概括為:事務在修改數據之前,需要先獲得相應的鎖;獲得鎖之后,事務便可以修改數據;該事務操作期間,這部分數據是鎖定的,其他事務如果需要修改數據,需要等待當前事務提交或回滾后釋放鎖。(共享鎖和獨占鎖)
如何對讀加鎖參考4.0
后續展望
由于鎖機制和MVCC也屬于MySQL的核心難點,我們這里就不在過多闡述,詳情見后續文章。
1.后續之MVCC詳解
2.后續之MVCC是否能解決幻讀&next-key lock解決幻讀
5.C一致性實現原理
一致性定義
一致性是指事務執行的結果必須是使數據庫從一個一致性狀態變到另一個一致狀態,即數據庫的完整性沒有被破壞,事務執行的前后都是合法的狀態。數據庫的完整性包括但不限于:實體完整性(如行的主鍵存在且唯一)、列完整性(如字段的類型、大小、長度要符合要求)、外鍵約束、用戶自定義完整性。
實現
可以說,一致性是事務追求的最終目標:前面提到的原子性、持久性和隔離性,都是為了保證數據庫狀態的一致性。此外,除了數據庫層面的保障,一致性的實現也需要應用層面進行保障。
實現一致性的措施包括:
- 保證原子性、持久性和隔離性,如果這些特性無法保證,事務的一致性也無法保證
- 數據庫本身提供保障,例如不允許向整形列插入字符串值、字符串長度不能超過列的限制等
- 應用層面進行保障,例如如果轉賬操作只扣除轉賬者的余額,而沒有增加接收者的余額,無論數據庫實現的多么完美,也無法保證狀態的一致
6.總結
參考
下面總結一下ACID特性及其實現原理:
- 原子性:語句要么全執行,要么全不執行,是事務最核心的特性,事務本身就是以原子性來定義的;實現主要基于undo log
- 持久性:保證事務提交后不會因為宕機等原因導致數據丟失;實現主要基于redo log
- 隔離性:保證事務執行盡可能不受其他事務影響;InnoDB默認的隔離級別是RR(可重復讀),RR的實現主要基于鎖機制(包含next-key lock)、MVCC(包括數據的隱藏列、基于undo log的版本鏈、ReadView)
- 一致性:事務追求的最終目標,一致性的實現既需要數據庫層面的保障,也需要應用層面的保障
總結
以上是生活随笔為你收集整理的MySQL事务实现原理详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: HTML5七夕情人节表白网页(雪花爱心表
- 下一篇: Spring事务和MySQL事务详解面试