關於事務,是一個很重要的知識點,你們在面試中也會被常常問到這個問題;mysql
數據庫事務有不一樣的隔離級別,不一樣的隔離級別對鎖的使用是不一樣的,鎖的應用最終致使不一樣事務的隔離級別;在上一篇文章中咱們說到了數據庫鎖的一部分知識,知道了InnoDB是支持行鎖的,可是走行鎖是基於索引的;面試
這裏咱們會說一下和鎖緊密相關的事務;sql
但願本文對你們有所幫助;數據庫
本文參考文章:數據庫的兩大神器併發
數據庫事務有不一樣的隔離級別,不一樣的隔離級別對鎖的使用是不一樣的,鎖的應用最終致使不一樣事務的隔離級別;mvc
關於事務,你們也是比較熟悉的,在這裏咱們再來嘮叨一下:post
說到事務,就不得不提它的特性以及隔離級別了;學習
事務具備四個特性:原子性、一致性、隔離性、持久性。這四個屬性一般被稱爲ACID屬性。spa
對於以上的四個特性,咱們來拿經典的轉帳的例子來講明;.net
有A和B兩我的,如今A須要往B的帳戶上轉錢,通常的操做是這樣:
以上是轉帳的操做步驟,咱們來講明一下事務的四大特性:
以上的六步操做要麼所有執行,要麼所有不執行。無論執行到那一步出現了問題,就須要執行回滾操做;
在轉帳以前,A和B的帳戶加一塊兒500 + 500 = 1000 元,在轉帳以後A和B的帳戶加起來是400 + 600 = 1000。也就是說,數據的狀態在執行該事務操做以後從一個狀態改變到了另一個狀態,須要保持一致性;
在A向B轉帳的過程當中,只要所處事務尚未提交,其餘事務查詢A或者B帳戶的時候,兩個帳戶的金額都不會發生變化;
若是在A給B轉帳的同時,有另一個事務執行了C給B轉帳的操做,那麼當兩個事務都結束的時候,B帳戶裏面的錢應該是A轉給B的錢加上C轉給B的錢再加上本身原有的錢;
一旦轉帳成功,事務提交,所作的修改就會永久的保存;
參考文章:www.hollischuang.com/archives/89…
咱們對於事務的隔離級別也是很清楚的,分爲四種:
在Read uncommitted隔離級別下會出現髒讀,咱們先來看一下髒讀;
髒讀:一個事務讀取到另外一個事務未提交的數據的狀況被稱爲髒讀。
舉例說明:
仍是拿轉帳的例子做爲說明。A向B轉帳,A執行了轉帳語句,但A尚未提交事務,B讀取數據,發現本身帳戶錢變多了!B跟A說,我已經收到錢了。A回滾事務【rollback】,等B再查看帳戶的錢時,發現錢並無多。
分析:
出現髒讀的本質就是由於操做(修改)完該數據就立馬釋放掉鎖,致使讀的數據就變成了無用的或者是錯誤的數據。
解決(Read committed):
從上面的分析也能看出來,解決的方式就是把鎖釋放的位置放到事務提交以後 。這樣的話,在事務還未提交以前,其餘的事務對該數據是沒法進行操做的,這也是Read committed
避免髒讀的作法;
Read committed
雖然避免了髒讀可是會出現不可重複讀;
不可重複讀:一個事務讀取到另一個事務已經提交的數據,也就是說一個事務能夠看到其餘事務所作的修改 ;
舉例說明:
事務A在讀取一條數據,獲得結果a,事務B把這條數據改爲了b並提交了事務,這個時候事務A再次去讀取這條數據,獲得的結果是b。這樣就發生了不可重複讀;
分析:
Read committed
採用的是語句級別的快照!每次讀取的都是當前最新的版本!
解決:
Repeatable read
避免不可重複讀是事務級別的快照!每次讀取的都是當前事務的版本,即便被修改了,也只會讀取當前事務版本的數據。
這裏涉及到了快照一詞,咱們須要說一下這個東西:
MVCC(Multi-Version Concurrency Control):多版本併發控制 。經過必定機制生成一個數據請求時間點的一致性數據快照(Snapshot),並用這個快照來提供必定級別(語句級或事務級)的一致性讀取。從用戶的角度來看,好像是數據庫能夠提供同一數據的多個版本。一句話總結就是 同一份數據臨時保留多版本的一種方式,進而實現併發控制 ;
快照有兩個級別:
Read committed
隔離級別Repeatable read
隔離級別InnoDB 的 MVCC, 是經過在每行記錄後面保存兩個隱藏的列來實現的, 這兩個列,分別保存了這個行的建立時間,一個保存的是行的刪除時間。這裏存儲的並非實際的時間值, 而是系統版本號 (能夠理解爲事務的 ID),每次開始一個新的事務,系統版本號就會自動遞增;當刪除一條數據的時候,該數據的刪除時間列就會存上當前事務的版本號 ;事務開始時刻的系統版本號會做爲事務的 ID;
下面看一下在 REPEATABLE READ 隔離級別下, MVCC 具體是如何操做的;
首先建立一個表:
CREATE TABLE `mvcc` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`username` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB CHARSET=utf8;
複製代碼
假設系統版本號從1開始;
InnoDB 爲新插入的每一行保存當前系統版本號做爲版本號,上面咱們假設系統版本號從1開始;
start transaction;
INSERT INTO `mvcc`(username) VALUES ('tom'),('joey'),('James');
commit;
複製代碼
獲得以下結果(後面兩列是隱藏的,經過查詢語句看不到):
id | username | 建立時間 (事務 ID) | 刪除時間 (事務 ID) |
---|---|---|---|
1 | tom | 1 | undefined |
2 | joey | 1 | undefined |
3 | james | 1 | undefined |
InnoDB會根據如下兩個條件檢查每條記錄:
以上兩個條件同時知足的狀況下,才能做爲結果返回;
InnoDB 會爲刪除的每一行保存當前系統的版本號 (事務的 ID) 做爲刪除標識;
具體例子:
第二個事務,系統版本號爲2;
start transaction;
select * from mvcc; //step 1
select * from mvcc; //step 2
commit;
複製代碼
狀況一
第三個事務,系統版本號爲3;
start transaction;
INSERT INTO `mvcc`(username) VALUES ('yang');
commit;
複製代碼
當咱們執行step 1剛完畢,這個時候第三個事務往表中插入了一條數據,這個時候表中的數據以下:
id | username | 建立時間 (事務 ID) | 刪除時間 (事務 ID) |
---|---|---|---|
1 | tom | 1 | undefined |
2 | joey | 1 | undefined |
3 | james | 1 | undefined |
4 | yang | 3 | undefined |
而後step 2執行了,獲得以下結果:
id | username | 建立時間 (事務 ID) | 刪除時間 (事務 ID) |
---|---|---|---|
1 | tom | 1 | undefined |
2 | joey | 1 | undefined |
3 | james | 1 | undefined |
你們可能會感到迷惑,第三個事務不是往裏面插入了一條數據嗎,怎麼查不到。這個時候咱們來講一下緣由:
狀況二
第四個事務,系統版本爲4:
start transaction;
delete from mvcc where id=1;
commit;
複製代碼
當第二個事務執行了step 1,這個時候第三個事務的插入也執行完畢了,接着事務四開始執行,此時數據庫的數據以下:
id | username | 建立時間 (事務 ID) | 刪除時間 (事務 ID) |
---|---|---|---|
1 | tom | 1 | 4 |
2 | joey | 1 | undefined |
3 | james | 1 | undefined |
4 | yang | 3 | undefined |
上面能夠看出,當執行DELETE操做的時候,刪除時間(事務ID)列會存上當前事務的系統版本號;
而後step 2執行了,獲得以下結果:
id | username | 建立時間 (事務 ID) | 刪除時間 (事務 ID) |
---|---|---|---|
1 | tom | 1 | 4 |
2 | joey | 1 | undefined |
3 | james | 1 | undefined |
具體緣由我就不說了(SELECT查詢的兩個條件);
InnoDB 執行 UPDATE,其實是新插入的一行數據 ,並保存其建立時間(事務ID)爲當前事務的系統版本號,同時保存當前事務系統版本號到須要UPDATE的行的刪除時間(事務ID) ;
狀況三
第五個事務,系統版本號爲5:
start transaction;
update mvcc set name='jack' where id = 3;
commit;
複製代碼
當執行完step 1,第三個的插入和第四個事務的刪除都執行完畢而且提交,又有一個用戶執行了第五個事務的更新操做,這個時候,數據庫數據以下:
id | username | 建立時間 (事務 ID) | 刪除時間 (事務 ID) |
---|---|---|---|
1 | tom | 1 | 4 |
2 | joey | 1 | undefined |
3 | james | 1 | 5 |
4 | yang | 3 | undefined |
3 | jack | 5 | undefined |
而後咱們執行step 2獲得以下數據:
id | username | 建立時間 (事務 ID) | 刪除時間 (事務 ID) |
---|---|---|---|
1 | tom | 1 | 4 |
2 | joey | 1 | undefined |
3 | james | 1 | 5 |
以上幾種狀況能夠看出,無論咋樣,查出的數據都是和第一次查詢的數據一致,儘管其餘事務作了各類修改操做,可是沒有影響到第二個事務中的查詢操做;
經過以上對MVCC的介紹,我想你們也明白了Repeatable read
避免不可重複讀的方式;
參考文章:blog.csdn.net/whoamiyang/…
幻讀:是指在一個事務內讀取到了別的事務插入的數據,致使先後讀取不一致 (幻讀是事務非獨立執行時發生的一種現象);
舉例說明:
例如事務A對一個表中符合條件的一些數據作了從a修改成b的操做,這時事務B又對這個表中插入了符合A修改條件的一行數據項,而這個數據項的數值仍是爲a而且提交給數據庫。而操做事務A的用戶若是再查看剛剛修改的數據,會發現還有一行沒有修改,其實這行是從事務B中添加的,就好像產生幻覺同樣,這就是發生了幻讀。
解決:
但在MySQL實現的Repeatable read配合間隙鎖不會出現幻讀;
使用間隙鎖鎖住符合條件的部分,不容許插入符合條件的數據。
間隙鎖:當咱們用範圍條件檢索數據而不是相等條件檢索數據,並請求共享或排他鎖時,InnoDB會給符合範圍條件的已有數據記錄的索引項加鎖;對於鍵值在條件範圍內但並不存在的記錄,叫作「間隙(GAP)」。InnoDB也會對這個「間隙」加鎖,這種鎖機制就是所謂的間隙鎖(間隙鎖只會在Repeatable read
隔離級別下使用)。
InnoDB使用間隙鎖的目的有兩個:
本文介紹了MySQL數據鎖以及事務的一些知識點,下面咱們來總結一下;
事務的四大特性:
對於事務的隔離級別也是很清楚的,分爲四種:
MVCC(Multi-Version Concurrency Control):多版本併發控制 ,一句話總結就是 同一份數據臨時保留多版本的一種方式,進而實現併發控制 (上面也簡單的演示了InnoDB MVCC的實現);
MVCC可以實現讀寫不阻塞 ;
快照有兩個級別:
Read committed
隔離級別Repeatable read
隔離級別Repeatable read
避免不可重複讀是事務級別的快照!每次讀取的都是當前事務的版本,即便被修改了,也只會讀取當前事務版本的數據。
本文簡單的說了一下事務一塊的東西,有問題的話還望你們指教,本人必定抱着虛心學習的態度。
你們共同窗習,一塊兒進步!