秋招告一段落,浪了很久也該總結總結了,雖然面試這一類別下絕大部分都是參考的別的博客,但本身再整理一遍總感受印象更深吧。對於參考的原文都有代表原文連接html
-----------------------------------------------------------------------------------------------面試
轉自:https://www.cnblogs.com/hebao0514/category/719525.html數據庫
1、事務的四大特性(ACID)併發
1. 原子性(atomicity):一個事務必須視爲一個不可分割的最小工做單元,整個事務中的全部操做要麼所有提交成功,要麼所有失敗回滾,對於一個事務來講,不可能只執行其中的一部分操做,這就是事務的原子性。性能
2. 一致性(consistency):數據庫老是從一個一致性的狀態轉換到另外一個一致性的狀態。
拿轉帳來講,假設用戶A和用戶B二者的錢加起來一共是5000,那麼無論A和B之間如何轉帳,轉幾回帳,事務結束後兩個用戶的錢相加起來應該還得是5000,這就是事務的一致性。atom
3. 隔離性(isolation):一個事務所作的修改在最終提交之前,對其餘事務是不可見的。
好比操做同一張表時,數據庫爲每個用戶開啓的事務,不能被其餘事務的操做所幹擾,多個併發事務之間要相互隔離。spa
4. 持久性(durability):一旦事務提交,則其所作的修改就會永久保存到數據庫中。此時即便系統崩潰,修改的數據也不會丟失。命令行
2、事務的簡單使用
1. 在使用數據庫時候須要使用事務,必須先開啓事務,開啓事務的語句具體以下:線程
start transaction;
2. 事務開啓以後就能夠執行SQL語句
3. SQL語句執行成功以後,須要提交事務,提交事務的語句以下:3d
commit;
note:
在MySQL中直接書寫的SQL語句都是自動提交的,而事務中的操做語句須要使用commit語句手動提交,只有事務提交後其中的操做纔會生效。
若是不想提交事務,咱們還可使用相關語句取消事務(也稱回滾),具體語句以下:
rollback;
須要注意的是,rollback語句只能針對未提交的事務執行的回滾操做,已經提交的事務是不能回滾的。
例子:經過一個轉帳的案例演示如何使用事務。
1. 建立表
create table account( id int primary key auto_increment, name varchar(40), money float ); insert into account(name,money) values('a',1000); insert into account(name,money) values('b',1000);
2. 使用事務來實現轉帳:首先開啓一個事務,而後經過update語句將 a帳戶的100元 轉給 b 帳戶,而後提交事務
start transaction; update account set money=money-100 where name='a'; update account set money=money+100 where name='b'; commit;
1). 在命令行中執行,執行效果以下:
2). 在Navicat中執行,執行結果以下:
參考:
http://www.javashuo.com/article/p-wszauznq-s.html
3、髒讀
(一)、什麼是髒讀
髒讀就是指一個事務讀取了另外一個事務未提交的數據。
(二)、髒讀的例子及避免
(1). 髒讀例子:
髒讀例子:http://www.javashuo.com/article/p-rduteysi-en.html Read Uncommitted(讀未提交)(我的理解:一個事務讀到另外一個事務還未提交的數據)
例子說明:開啓兩個線程,分別模擬a帳戶和b帳戶,
MySQL的默認隔離級別是Repeatable Read(可重複讀),該級別是能夠避免髒讀的,所以須要將b帳戶中事務的隔離級別設置爲Read Uncommitted(讀未提交)。
在a帳戶中開啓一個事務,執行轉帳功能,但未提交(commit);在b帳戶中開啓一個事務,執行查詢功能,由於b帳戶的事務隔離級別低,
就讀到了a帳戶還沒提交的數據(即出現髒讀),這時候,b誤覺得a帳戶已經轉帳成功,便會給a發貨,當b發貨以後,a若是不提交事務而將事務回滾,b就會受到損失。
(2). 避免髒讀:Read Committed 隔離級別來避免髒讀的例子:
1. a帳戶(左)和b帳戶(右)當前餘額:
2. 開啓事務,轉帳給b帳戶:
3. 此時a帳戶的事務並未提交,此時b帳戶查看餘額:
能夠看出,b帳戶中仍爲1000,沒有讀到a帳戶中沒有提交的信息。說明Read Committed 隔離級別能夠避免髒讀
4、事務隔離級別
https://www.cnblogs.com/hebao0514/p/5492108.html
5、不可重複讀
(一)、什麼是不可重複讀
不可重複讀(Non-Repeatable Read)是指事務中兩次查詢的結果不一致,緣由是在查詢的過程當中其餘事務作了更新的操做。
例如,銀行在作統計報表的時候,第一次查詢a帳戶有1000元,第二次查詢a帳戶有900元,緣由是統計期間a帳戶取出了100元,這樣致使屢次查詢中,查詢結果不一致。
不可重複讀和髒讀有點相似,可是髒讀是讀取了另外一個事務未提交的髒數據,不可重複讀是在事務內重複讀取了別的線程已提交的數據。
note:MySQL的默認事務隔離級別是:Repeatable Read(可重複讀)
(二)、不可重複讀的例子及避免
(1). 不可重複讀的例子
不可重複讀的例子:http://www.javashuo.com/article/p-vkscqcbu-ec.html Repeatable Read(可重複讀)(我的理解:一個事務中,重複查詢的結果是同樣的,哪怕在第一次查詢以後,數據庫已經變了,查詢結果仍與第一次查詢結果一致,而不是查詢數據庫中的最新數據)
1. 線程2:查看線程2中事務隔離級別:
1. 線程2:首先在線程2中開啓一個事務,而後在當前事務中查詢各個帳戶的餘額信息:
3. 線程1:線程1中不用開啓事務,直接使用update更新a帳戶,並查詢餘額:
note:因爲線程1只須要執行修改的操做,不須要保證同步性,所以直接執行SQL語句就能夠
4. 線程2:當線程1更新操做執行完成後,在線程2中再次查詢各帳戶餘額,發現a帳戶變爲900:
線程2中,a帳戶兩次的查詢結果不一致,實際上這種操做是沒有錯的(雖然沒錯,但應該避免這種狀況?)
例子說明:開啓兩個線程1和2,線程2的隔離級別爲Read Committed。在線程2中開啓事務,查詢a帳戶爲1000,此時還不提交事務;在線程1中不用開啓事務,更新a帳務爲900;返回線程2(此時事務尚未提交),查詢a帳戶變爲900。即:線程2在同一個事務中,兩次查詢結果不一致。(博客例子中的a帳戶和b帳戶應該爲表述錯誤,應爲:線程1和線程2,操做的帳戶都是a帳戶。另外博客的線程2(即b帳戶中)繼前一篇博客的操做,已經改爲了Read Committed 隔離級別)
(2). 避免不可重複讀:Repeatable Read 隔離級別來避免不可重複讀的例子:
1. 查看線程1中事務隔離級別:
2. 在線程1中開啓一個事務,而後在當前事務中查詢各個帳戶的餘額信息:
3. 線程2中不用開啓事務,直接使用update語句執行更新操做,並查詢餘額:
4. 返回線程1:當線程2更新操做執行完成後,在線程1中再次查詢帳戶餘額,發現a帳戶仍未1000:
5. 若是此時在線程1中修改a帳戶,會報錯:ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
6、幻讀
(一)、什麼是幻讀
幻讀(Phantom Read)又稱爲虛讀,是指在一個事務內兩次查詢中數據條數不一致,幻讀和不重複讀有些不一樣,一樣是在兩次查詢過程當中,不一樣的是,幻讀是因爲其餘事務作了插入記錄的操做,致使記錄數有所增長。
例如:銀行在作統計報表時統計account表中全部用戶的總金額時候,此時總共有兩個帳戶,總共金額爲2000元,這時候新增了一個用戶帳戶,而且存入1000元,這時候銀行再次統計就會發現帳戶總金額爲3000,形成了幻讀狀況
(二)、幻讀的例子及避免
(1). 幻讀的例子
幻讀的例子:http://www.javashuo.com/article/p-fvhaovcx-es.html Phantom Read(幻讀、虛讀)(我的理解:一個事務中,重複查詢的結果是同樣的,哪怕在第一次查詢以後,數據庫已經變了,查詢結果仍與第一次查詢結果一致,而不是查詢數據庫中的最新數據。幻讀是針對insert操做)
1. 線程2:首先設置線程2的隔離級別爲Read Committed,並查詢。(可重複讀隔離級別是能夠避免幻讀的出現,所以須要將事務的隔離級別設置爲更低)
2. 線程2:開啓一個事務,而後在當前事務中查詢帳戶的餘額信息:
3. 線程1:先查詢account表中的信息,而後進行添加操做:(線程1不用開啓事務,直接執行添加操做便可)
4. 線程2:線程1添加完記錄後,在線程2中查詢餘額信息:
能夠發現,在Read Committed隔離級別下,線程2中第二次查詢數據比第一查詢數據的時候多一條記錄,這種狀況並非錯誤的,但可能不符合實際需求。
例子說明:開啓兩個線程1和2,線程2的隔離級別爲Read Committed。在線程2中開啓事務,查詢account表的結果爲共有兩條記錄;在線程1中不用開啓事務,在線程1中添加一條記錄(c,1000);返回線程2,查詢account表,查詢結果變爲共有三條記錄。即:線程2在同一個事務中,兩次查詢結果不一致。
幻讀和不重複讀的區別:
一樣在兩次查詢過程當中,不一樣的是,幻讀是因爲其餘事務作了插入操做(insert),致使記錄數有所增長。而不重複讀是因爲其餘事務作了更新操做(update),致使同一條記錄的查詢結果不一樣。
(2). 避免幻讀:Repeatable Read 隔離級別來避免幻讀的例子:
先刪除剛纔添加的(c,1000)這一條記錄
1. 線程2:爲了防止出現幻讀,能夠將線程2的隔離級別設置爲Repeatable Read
2. 線程2:開啓一個事務,而後在當前事務中查詢帳戶的餘額信息:
3. 線程1:先查詢account表中的信息,而後進行添加操做:(線程1不用開啓事務,直接執行添加操做便可)
4. 線程2:線程1添加完記錄後,在線程2中查詢餘額信息
能夠發現,在Repeatable Read隔離級別下,線程2中兩次查詢結果是同樣的。
5. 線程2:使用commit提交當前事務,再查詢account表,查詢到三條記錄
Repeatable Read從理論的角度是會出現幻讀的,可是MySQL內部經過多版本控制機制【實際上就是對讀取到的數據加鎖】解決這個問題。
所以,用戶才能夠放心大膽使用Repeatable Read這個事務隔離級別。
note:Serializable 和 Repeatable Read均可以防止幻讀。可是Serializable 事務隔離級別效率低下,比較耗數據庫性能,通常不使用。