事務的四大特性(ACID):
1.原子性(atomicity):一個事務必須視為一個不可分割的最小工作單元,整個事務中的所有操作要么全部提交成功,要么全部失敗回滾,對於一個事務來說,不可能只執行其中的一部分操作,這就是事務的原子性。
2.一致性(consistency):數據庫總數從一個一致性的狀態轉換到另一個一致性的狀態。
3.隔離性(isolation):一個事務所做的修改在最終提交以前,對其他事務是不可見的。
4.持久性(durability):一旦事務提交,則其所做的修改就會永久保存到數據庫中。此時即使系統崩潰,修改的數據也不會丟失。
ACID:
- 原子性: 語句要么都執行,要么都不是執行,是事務最核心的特性,事務本身來說就是以原子性歷來定義的,實現主要是基於undo log
- 持久性: 保證事務提交之后,不會因為宕機等其他的原因而導致數據的丟失,主要是基於 redo log實現
- 隔離性: 保證事務與事務之間的執行是相互隔離的,事務的執行不會受到其他事務的影響。InnoDB存儲引擎默認的數據庫隔離級別是 RR(可重復讀) ,RR又主要是基於鎖機制,數據的隱藏列,undo log類 以及 next-key lock機制
- 一致性: 事務追求的最終目標,一致性的實現即需要數據庫層面的保障,也需要應用層面的保障。
數據庫事務的隔離級別有4種,由低到高分別為Read uncommitted 、Read committed 、Repeatable read (默認)、Serializable 。
臟讀:一個事物看到另一個事物未提交的數據
不可重復讀:一個事物范圍內,兩次查詢到的數據不一致(針對於update)。
幻讀:一個事物范圍內,兩次查詢到的數據不一致(針對的是insert)。
Read uncommitted:未提交讀,啥也避免不了
Read committed :讀提交,若有事務對數據進行更新(UPDATE)操作時,讀操作事務要等待這個更新操作事務提交后才能讀取數據,可以解決臟讀問題
Repeatable read:可重復讀,解決了不可重復讀。
Serializable:在該級別下,事務串行化順序執行,可以避免臟讀、不可重復讀與幻讀。但是這種事務隔離級別效率低下,比較耗數據庫性能,一般不使用。
mysql數據庫中的三種日志:redolog:保證的是數據的持久性,undolog:保證的是數據的隔離性以及原子性。
1、為何引入redolog ,redo log如何保證持久性?
當數據修改時,除了修改Buffer Pool中的數據,還會在redo log記錄這次操作;當事務提交時,會調用fsync接口對redo log進行刷盤。如果MySQL宕機,重啟時可以讀取redo log中的數據,對數據庫進行恢復。redo log采用的是WAL(Write-ahead logging,預寫式日志),所有修改先寫入日志,再更新到Buffer Pool,保證了數據不會因MySQL宕機而丟失,從而滿足了持久性要求。
2、既然redo log也需要在事務提交時將日志寫入磁盤,為什么它比直接將Buffer Pool中修改的數據寫入磁盤(即刷臟)要快呢?主要有以下兩方面的原因:
(1)刷臟是隨機IO,因為每次修改的數據位置隨機,但寫redo log是追加操作,屬於順序IO。
(2)刷臟是以數據頁(Page)為單位的,MySQL默認頁大小是16KB,一個Page上一個小修改都要整頁寫入;而redo log中只包含真正需要寫入的部分,無效IO大大減少。
3、redo log 與 binlog
我們知道,在MySQL中還存在binlog(二進制日志)也可以記錄寫操作並用於數據的恢復,但二者是有着根本的不同的:
(1)作用不同:redo log是用於crash recovery的,保證MySQL宕機也不會影響持久性;binlog是用於point-in-time recovery的,保證服務器可以基於時間點恢復數據,此外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、隔離性的探討,主要可以分為兩個方面:
- (一個事務)寫操作對(另一個事務)寫操作的影響:鎖機制保證隔離性
- (一個事務)寫操作對(另一個事務)讀操作的影響:MVCC保證隔離性
5、鎖機制:表鎖和行鎖
鎖機制的基本原理可以概括為:事務在修改數據之前,需要先獲得相應的鎖;獲得鎖之后,事務便可以修改數據;該事務操作期間,這部分數據是鎖定的,其他事務如果需要修改數據,需要等待當前事務提交或回滾后釋放鎖。
行鎖與表鎖
按照粒度,分為表鎖與行鎖:
MyISAM 支持表鎖,InnoDB 支持表鎖和行級鎖,默認是行級鎖。表級鎖:開銷小,加鎖快,不會出現死鎖。鎖定粒度大,發送鎖沖突的概率比較高,並發處理效果較低。行級鎖: 開銷大,加鎖慢,會出現死鎖,鎖定粒度較小,發生鎖沖突的概率會小一點,並發處理的效果高。
6、MVCC
前面講到了 RR(可重復讀)解決了 臟讀,不可重復讀,幻讀等問題使用到的就是MVCC(Multi-Version Concurrency Control) 既多版本的並發控制:在同一個時刻,不同的事物讀取到的數據可能是不同的(多版本)。對於MVCC來說最大的優點就是讀不加鎖,因此讀寫不沖突,並發性能好
InnoDB實現MVCC,多個版本的數據可以共存,主要是依靠數據的隱藏列(也可以稱之為標記位)和undo log。其中數據的隱藏列包括了該行數據的版本號、刪除時間、指向undo log的指針等等;當讀取數據時,MySQL可以通過隱藏列判斷是否需要回滾並找到回滾需要的undo log,從而實現MVCC。
7、
如何解決幻讀
nnoDB實現的RR(可重復讀)通過next-key lock機制避免了幻讀現象。
next-key lock是行鎖的一種,實現相當於record lock(記錄鎖) + gap lock(間隙鎖);其特點是不僅會鎖住記錄本身(record lock的功能),還會鎖定一個范圍(gap lock的功能)。當然,這里我們討論的是不加鎖讀:此時的next-key lock並不是真的加鎖,只是為讀取的數據增加了標記(標記內容包括數據的版本號等);
