目錄
内容概覽
事務是訪問并更新數據庫中各種數據項的一個程序執行單元。在事務中的操作,要麼都執行修改,要麼都不執行,這就是事務的目的,也是事務模型區别于文件系統的重要特征之一。
MySQL Server系統結構包括三層:SQL層、存儲引擎層和物理文件,事務操作是在存儲引擎層完成的。
MySQL Server系統結構
MySQL數據庫支持多種存儲引擎,有InnoDB,MyISAM,NDB,Memory等,并不是所有存儲引擎都支持事務操作。
本文介紹InnoDB存儲引擎對事務的支持。《MySQL技術内幕》對InnoDB存儲引擎的介紹:“InnoDB通過使用多版本并發控制(MVCC)來獲得高并發性,并且實現了SQL标準的4種隔離級别,默認為REPEATABLE(可重複讀)級别,同時使用一種稱為netx-key locking的策略來避免幻讀(phantom)現象的産生。”
一、事務特性(ACID)1)原子性 Atomicity事務是一個原子操作,事務中的操作要麼都執行,要麼都不執行,任何一個SQL語句執行失敗 ,那麼執行成功的SQL也必須撤銷,數據庫狀态應該退回到執行事務前的狀态。
2)一緻性 Consistency一緻性指事務操作前和操作後都必須滿足業務規則約束,數據庫的完整性約束沒有被破壞。
3)隔離性 Isolation隔離性也稱為并發控制(concurrency control)、可串行化(serializability)、鎖(locking)。多個事務之間的操作是相互隔離的,即該事務提交前對其他事務都不可見,通常使用鎖來實現。
4)持久性 Durability事務一旦提交,其結果是永久性的,即使發生宕機等故障,數據庫也能将數據恢複。需要注意的是,持久性隻能從事務本身的角度來保證結果的永久性,如果不是數據庫本身發生故障,而是一些外部的原因,如RAID卡損壞、自然災害等導緻數據庫發生問題,那麼所有提交的數據可能會丢失。
二、事務并發問題由于對數據庫的操作不同,事務之間并不是順序執行,而是并發執行的,事務并發可能會帶來如下問題:
1)更新丢失 (Lost Update)當多個事務操作同一行數據,由于每個事務都不知道其他事務的存在,更新數據時,一個事務的結果可能會被其他事務覆蓋,發生丢失更新的問題。
2)髒讀(Dirty Reads)如果一個事務對數據進行了更新,但事務還沒有提交,另一個事務讀到了該事務沒有提交的數據,那麼如果第一個事務回滾,第二個事務讀到的數據就是髒數據。
3)不可重複讀(Non-Repeatable Reads)——針對同一數據指在一個事務内兩次讀到的數據是不一樣的。比如事務A讀取某一數據,事務B修改了該數據,事務A為了對數據進行驗證而再次讀取該數據,便得到了不同的結果。
一種更易理解的說法是:在一個事務内,多次讀同一個數據。在這個事務還沒有結束時,另一個事務也訪問同一數據并修改數據。那麼,在第一個事務的兩次讀數據之間,由于另一個事務的修改,第一個事務兩次讀到的數據可能不一樣,這樣就發生了在一個事務内兩次讀到的數據是不一樣的,因此稱為不可重複讀,即原始讀取不可重複。
4)幻讀(Phantom Reads) ——針對多條數據幻讀是指同樣一個查詢在整個事務過程中多次執行後,查詢的結果集是不一樣的,也就是事務中讀取到了其他事務新增的數據,仿佛出現了幻象。不符合事務的隔離性,幻讀針對的是多條記錄。
三、事務隔離級别并發事務帶來的“更新丢失”問題,通常是可以完全避免的。防止更新丢失,并不能單靠數據庫事務控制器來解決,需要應用程序對要更新的數據加必要的鎖來解決。
“髒讀”、“不可重複讀”和“幻讀”,其實都是數據庫讀一緻性問題,必須由數據庫提供一定的事務隔離機制來解決。
為了解決“隔離”與“并發”的矛盾,ANSI SQL标準定義了 4個事務隔離級别,分别是:
READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ、SERIALIZABLE。
1)未提交讀(READ UNCOMMITTED)在一個事務中,可以讀取到其他事務未提交的數據變化,這種隔離級别會造成髒讀、不可重複讀、幻讀的問題。
2)已提交讀(READ COMMITTED)在一個事務中,可以讀取到其他事務已經提交的數據變化,這種隔離級别會造成不可重複讀、幻讀的問題。
例如事務A讀到了數據a, 事務B正好對數據a進行了更改,并且提交了。那麼事務A再次讀數據a時發現數據已改變。兩次同樣的查詢可能會得到不一樣的結果。
3)可重複讀(REPEATABLE READ)在一個事務中,直到事務結束前,都可以反複讀取到事務剛開始時看到的數據,并一直不會發生變化,避免了髒讀、不可重複讀問題,但無法解決幻讀問題。
4)可串行化(SERIALIZABLE)這是最高的隔離級别,它強制事務串行執行,不會出現髒讀、不可重複讀、幻讀問題。
5)隔離級别總覽
隔離級别 |
數據一緻性 |
髒讀 |
不可重複讀 |
幻讀 |
未提交讀 |
最低級别 |
是 |
是 |
是 |
已提交讀 |
語句級别 |
否 |
是 |
是 |
可重複讀 |
事務級别 |
否 |
否 |
是 |
可序列化 |
最高級别,事務級别 |
否 |
否 |
否 |
1)InnoDB存儲引擎使用MVCC實現了REPEATABLE READ(可重複讀),它的默認隔離級别也是REPEATABLE READ。
2)InnoDB存儲引擎在REPEATABLE READ事務隔離級别下,使用Next-Key Lock的鎖算法,避免了幻讀的産生。InnoDB存儲引擎已經能完全保證事務的隔離性要求,即達到SQL标準的SERIALIZABLE隔離級别。
實現事務隔離的方式,基本上可分為以下兩種。
下面分别介紹一下InnoDB存儲引擎的鎖和MVCC。
五、InnoDB鎖1)鎖的介紹MySQL 不同的存儲引擎支持不同的鎖機制,有3種粒度的鎖:
InnoDB存儲引擎既支持行級鎖,也支持表級鎖,但默認情況下是采用行級鎖。
InnoDB實現了兩種類型的行鎖:共享鎖、排他鎖。
共享鎖(S)——行鎖為了允許行鎖和表鎖共存,實現多粒度鎖機制,InnoDB 還有兩種内部使用的意向鎖(Intention Locks):意向共享鎖、意向排他鎖。這兩種意向鎖都是表鎖,意向鎖的主要作用是處理行鎖和表鎖之間的矛盾。
意向共享鎖(IS)——表鎖舉例說明:
事務A在更新行數據之前,先在表級别上加意向鎖,再加X鎖;加意向鎖是表示此表某一行被加了X鎖; 當事務B要對這個表加S鎖,如果表中數據很多,那麼事務B需要逐行檢查表是否能加S鎖,這樣會很耗時;如果此時在表級别上有意向鎖,那麼,事務B先檢查該表上是否存在意向鎖,存在的意向鎖是否與自己準備加的鎖沖突,如果有沖突,則等待直到事務A釋放,而無須逐條記錄去檢測,這樣速度會很快。
InnoDB行鎖模式兼容性
X鎖 |
IX鎖 |
S鎖 |
IS鎖 | |
X鎖 |
沖突 |
沖突 |
沖突 |
沖突 |
IX鎖 |
沖突 |
兼容 |
沖突 |
兼容 |
S鎖 |
沖突 |
沖突 |
兼容 |
兼容 |
IS鎖 |
沖突 |
兼容 |
兼容 |
兼容 |
InnoDB行鎖是通過給索引上的索引項加鎖來實現的,如果沒有索引,InnoDB将通過隐藏的聚簇索引來對記錄加鎖。InnoDB存儲引擎有3種行鎖算法。
Record lock單個行記錄上的鎖,通過對索引項加鎖實現,鎖住的是索引,而不是記錄本身。
數據庫并發時,一般是讀寫并發、寫-寫并發。MVCC主要用于處理讀-寫沖突,MVCC可以做到不加鎖,非阻塞并發讀。
基本原理MVCC是通過保存某個時間點的快照來實現的,快照數據是指該行之前的曆史版本數據。 InnoDB為每次修改保存一個版本,版本與事務時間戳關聯,每行記錄可能有多個版本。讀取快照數據是不需要上鎖的,因為沒有事務需要對曆史數據進行修改。
對于不同的事務隔離級别,讀取到的快照數據是不同的。
InnoDB非鎖定的一緻性讀
參考書籍《MySQL技術内幕:InnoDB存儲引擎(第2版)》
《MySQL技術内幕——SQL編程》
《深入淺出MySQL:數據庫開發、優化與管理維護(第2版)》
,更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!