一個事務會涉及大量的cpu操做和IO操做,這些操做會被打包成一個執行單元,要麼同時完成,要麼同時都不完成。面試
事務是一組原子性的sql命令或者說是一個獨立的工做單元,若是其中任何一條sql語句由於崩潰或者其餘緣由執行失敗,那麼該組全部的sql語句都不會執行。若是沒有顯示啓動事務,數據庫會根據autocommit的值,默認每條sql操做都會自動提交。sql
一個事務中的全部操做,要麼都完成,要麼都不執行,對於一個事務來講,不可能只執行其中的一部分。數據庫
數據庫老是從一個一致性的狀態轉換到另外一個一致性的狀態安全
一個事務所作的修改在最終提交以前,對其餘事務是不可見的。多個事務之間的操做相互不影響。每下降一個事務的隔離級別都能提升數據庫的併發,但同時不安全性就增長了。關於事務的隔離級別後續還要詳細討論。這裏簡單介紹一下:bash
事務一旦提交,就要更新到數據庫中,不能回滾。就算服務器宕機,仍然須要在下次啓動的時候自動執行事務中的sql命令,體現到數據庫中。服務器
隔離界別 | 髒讀 | 不可重複讀 | 幻讀 |
---|---|---|---|
未提交讀(Read uncommited) | 可能 | 可能 | 可能 |
已提交讀(Read commited) | 不可能 | 可能 | 可能 |
可重複讀(Repeatable read) | 不可能 | 不可能 | 可能 |
串行化(Serializable) | 不可能 | 不可能 | 不可能 |
準備一個帳戶表,表裏三個字段,分別是主鍵id,帳戶名和金額session
create table account(
id int(11) not null auto_increment,
customer_name varchar(255) not null,
money decimal(10,2) not null,
primary key(id),
unique uni_name using btree (customer_name)
) ENGINE = 'InnoDB' AUTO_INCREMENT=10 COMMENT = '帳戶表';
複製代碼
一、開啓兩個會話,設置隔離級別爲read uncommited併發
set session transaction isolation level read uncommitted;
select @@session.tx_isolation;
複製代碼
一、將會話1和會話2的隔離級別設置成讀提交模式性能
set session transaction isolation level read committed;
複製代碼
小結:在RC模式下,咱們發現,當另外一個事務沒有提交數據修改時,當前事務時讀不到修改後的數據的,這就避免了讀未提交模式的髒讀。但有一個問題,在當前事務中,兩次select的數據不同,這就存在了不可重複讀的問題。PS:RC隔離級別是Oracle數據庫默認的隔離級別。ui
一、將會話1和會話2的隔離級別都設置成可重複讀
set session transaction isolation level repeatable read;
複製代碼
首先說結論:在RR的隔離級別下,InnoDB使用MVCC和next-key locks(間隙鎖)解決幻讀。MVCC解決的是普通讀(快照讀)的幻讀,間隙鎖解決的是當前讀狀況下的幻讀。
(首先聲明:在RR模式下,引擎是InnoDB,沒法產生如下效果。)
一、事務1先執行,但未提交
start transaction;
update account set account=1000 where id>10;
複製代碼
結果爲:OK row xx表名成功影響多少行 二、事務2後執行,而且提交
start transaction;
insert into account(customer_name, money) values('李雲龍',10000);
commit;
複製代碼
三、事務1再select一下,結果集爲:
select * from account;
複製代碼
··· 李雲龍,10000
這時,事務1蒙了,不是已經將id>10的全部人的金額都更新成1000了嗎,怎麼多出一我的金額是10000?這是已提交事務2對事務1產生的影響,這個影響就叫作幻讀。
不可重複讀:屢次讀取一條記錄,發現該記錄中某些列值被修改過。
幻讀:只要是說屢次讀取一個範圍內的記錄(包括直接查詢全部記錄結果或者作聚合統計),發現結果不一致(通常指的是記錄增多,記錄的減小應該也算是幻讀)。
當前讀
所謂當前讀,指的是加鎖的select(S鎖或者X鎖),update或者delete語句。在RR的事務隔離級別下,數據庫會使用間隙鎖來鎖住本條記錄以及索引區間。例如:
select * from account where id>10 for update;鎖住的就是id=10的記錄以及id>10的這個區間範圍,避免範圍間插入記錄,以免產生幻影行記錄。
普通讀
由於普通讀是不會加鎖的讀,因此解決幻讀的手段是MVCC。MVCC會給每行元祖加一些輔助字段,記錄建立版本號和刪除版本號。在每個事務啓動的時候,都有一個惟一的遞增的版本號。每開啓一個新事務,事務的版本號就會遞增。MYSQL默認的隔離級別(可重複讀Repeatable Read)下,增刪改查就變成下面這樣:
id | name | createversion | deleteversion |
---|---|---|---|
1 | 馬雲 | 1 |
若是我把name更新成劉強東,事務id=2,那麼記錄就變成
id | name | createversion | deleteversion |
---|---|---|---|
1 | 馬雲 | 1 | 2 |
2 | 劉強東 | 2 |
=> 能夠看出,原來的元祖的deleteversion爲更新的這個事務的id,而且新增一條
若是刪除的話,假設事務id=3,刪除id=2的記錄
id | name | createversion | deleteversion |
---|---|---|---|
1 | 馬雲 | 1 | 2 |
2 | 劉強東 | 2 | 3 |
關鍵點來了,若是我如今讀取的話,須要同時知足兩個條件:
一、讀取建立版本小於或等於當前事務的版本號,這意味着數據在這個事務以前被建立
二、刪除版本爲空或者大於當前事務版本號的記錄。這意味着刪除操做在這個事務以後發生
因此當事務1插入一條記錄,事物2更新這條記錄並刪除這條記錄以後,事務1再次查詢這條記錄,只有id=1的記錄知足這兩個條件,因此結果爲:(避免了幻讀)
id | name | createversion | deleteversion |
---|---|---|---|
1 | 馬雲 | 1 | 2 |
全部事務串行執行,是最高的隔離級別,性能最差
這篇文章就到這了,總的來講數據庫事務的四個特性ACID和四種隔離級別(RU,RC,RR,串行化)以及會產生的問題(髒讀、不可重複讀、幻讀)是面試的高頻問點,什麼東西都須要積累,而後反覆的溫習,這樣才能記憶和理解的更深入。