go 切片
參考文件:https://www.cnblogs.com/kismetv/p/10331633.html
事務隔離級別
SQL標準中定義了四種隔離級別,並規定了每種隔離級別下上述幾個問題是否存在。一般來說,隔離級別越低,系統開銷越低,可支援的併發越高,但隔離性也越差。隔離級別與讀問題的關係如下:
事務(Transaction)是訪問和更新資料庫的程式執行單元;事務中可能包含一個或多個sql語句,這些語句要麼都執行,要麼都不執行。
典型的MySQL事務是如下操作的:
start
transaction
;
…… #一條或多條sql語句
commit
;
其中start transaction標識事務開始,commit提交事務,將執行結果寫入到資料庫。如果sql語句執行出現問題,會呼叫rollback,回滾所有已經執行成功的sql語句。當然,也可以在事務中直接使用rollback語句進行回滾。
MySQL中預設採用的是自動提交(autocommit)模式,如果沒有start transaction顯式地開始一個事務,那麼每個sql語句都會被當做一個事務執行提交操作。
ACID特性
ACID是衡量事務的四個特性:
- 原子性(Atomicity,或稱不可分割性)
- 一致性(Consistency)
- 隔離性(Isolation)
- 永續性(Durability)
MySQL的NDB Cluster事務不滿足永續性和隔離性;InnoDB預設事務隔離級別是可重複讀,不滿足隔離性;
原子性:指一個事務是一個不可分割的工作單位,其中的操作要麼都做,要麼都不做;如果事務中一個sql語句執行失敗,則已執行的語句也必須回滾,資料庫退回到事務前的狀態。
InnoDB實現回滾,靠的是undo log:當事務對資料庫進行修改時,InnoDB會生成對應的undo log;如果事務執行失敗或呼叫了rollback,導致事務需要回滾,便可以利用undo log中的資訊將資料回滾到修改之前的樣子。
undo log屬於邏輯日誌,它記錄的是sql執行相關的資訊。當發生回滾時,InnoDB會根據undo log的內容做與之前相反的工作:對於每個insert,回滾時會執行delete;對於每個delete,回滾時會執行insert;對於每個update,回滾時會執行一個相反的update,把資料改回去。
以update操作為例:當事務執行update時,其生成的undo log中會包含被修改行的主鍵(以便知道修改了哪些行)、修改了哪些列、這些列在修改前後的值等資訊,回滾時便可以使用這些資訊將資料還原到update之前的狀態。
永續性:指事務一旦提交,它對資料庫的改變就應該是永久性的。接下來的其他操作或故障不應該對其有任何影響。
實現原理:redo log
InnoDB作為MySQL的儲存引擎,資料是存放在磁碟中的,但如果每次讀寫資料都需要磁碟IO,效率會很低。為此,InnoDB提供了快取(Buffer Pool),Buffer Pool中包含了磁碟中部分資料頁的對映,作為訪問資料庫的緩衝:當從資料庫讀取資料時,會首先從Buffer Pool中讀取,如果Buffer Pool中沒有,則從磁碟讀取後放入Buffer Pool;當向資料庫寫入資料時,會首先寫入Buffer Pool,Buffer Pool中修改的資料會定期重新整理到磁碟中(這一過程稱為刷髒)。
Buffer Pool的使用大大提高了讀寫資料的效率,但是也帶了新的問題:如果MySQL宕機,而此時Buffer Pool中修改的資料還沒有重新整理到磁碟,就會導致資料的丟失,事務的永續性無法保證。
於是,redo log被引入來解決這個問題:當資料修改時,除了修改Buffer Pool中的資料,還會在redo log記錄這次操作;當事務提交時,會呼叫fsync介面對redo log進行刷盤。如果MySQL宕機,重啟時可以讀取redo log中的資料,對資料庫進行恢復。redo log採用的是WAL(Write-ahead logging,預寫式日誌),所有修改先寫入日誌,再更新到Buffer Pool,保證了資料不會因MySQL宕機而丟失,從而滿足了永續性要求。
既然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速度大大加快。
隔離性:事務內部的操作與其他事務是隔離的,併發執行的各個事務之間不能互相干擾。
那麼隔離性的探討,主要可以分為兩個方面:
- (一個事務)寫操作對(另一個事務)寫操作的影響:鎖機制保證隔離性
- (一個事務)寫操作對(另一個事務)讀操作的影響:MVCC保證隔離性
鎖機制
事務在修改資料之前,需要先獲得相應的鎖;獲得鎖之後,事務便可以修改資料;該事務操作期間,這部分資料是鎖定的,其他事務如果需要修改資料,需要等待當前事務提交或回滾後釋放鎖。
按照粒度,鎖可以分為表鎖、行鎖以及其他位於二者之間的鎖。表鎖在操作資料時會鎖定整張表,併發效能較差;行鎖則只鎖定需要操作的資料,併發效能好。但是由於加鎖本身需要消耗資源(獲得鎖、檢查鎖、釋放鎖等都需要消耗資源),因此在鎖定資料較多情況下使用表鎖可以節省大量資源。MySQL中不同的儲存引擎支援的鎖是不一樣的,例如MyIsam只支援表鎖,而InnoDB同時支援表鎖和行鎖,且出於效能考慮,絕大多數情況下使用的都是行鎖。
TODO......
----------------------------------------------------------------------------------------------
1)髒讀:當前事務(A)中可以讀到其他事務(B)未提交的資料(髒資料),這種現象是髒讀。舉例如下(以賬戶餘額表為例):
(2)不可重複讀:在事務A中先後兩次讀取同一個資料,兩次讀取的結果不一樣,這種現象稱為不可重複讀。髒讀與不可重複讀的區別在於:前者讀到的是其他事務未提交的資料,後者讀到的是其他事務已提交的資料。舉例如下:
(3)幻讀:在事務A中按照某個條件先後兩次查詢資料庫,兩次查詢結果的條數不同,這種現象稱為幻讀。不可重複讀與幻讀的區別可以通俗的理解為:前者是資料變了,後者是資料的行數變了。舉例如下:
事務隔離級別
SQL標準中定義了四種隔離級別,並規定了每種隔離級別下上述幾個問題是否存在。一般來說,隔離級別越低,系統開銷越低,可支援的併發越高,但隔離性也越差。
read uncommitted 讀未提交 所有事務都可以看到沒有提交事務的資料。
read committed 讀已提交讀 事務成功提交後才可以被查詢到。
可重複讀
- 事務A和事務B,事務A提交之後的資料,事務B讀取不到
- 事務B是可重複讀取資料
- 這種隔離級別高於讀已提交
- 換句話說,對方提交之後的資料,我還是讀取不到
- 這種隔離級別可以避免“不可重複讀取”,達到可重複讀取
- 比如1點和2點讀到資料是同一個
- MySQL預設級別
- 雖然可以達到可重複讀取,但是不可避免“幻讀”
序列化
- 事務A和事務B,事務A在操作資料庫時,事務B只能排隊等待
隔離級別與讀問題的關係如下: