1. 程式人生 > >【MySQL】:事務四大特性與隔離級別

【MySQL】:事務四大特性與隔離級別

[toc] # 一、事務的概念 > 什麼是事務呢? 事務是由一步或幾步資料庫操作序列組成的邏輯執行單元,這系列操作**要麼全部執行,要麼全部放棄執行**。 # 二、事務的四大特性 原子性(Atomic),一致性(Consistency),隔離性(Isolation),持續性(Durability),簡稱ACID性。 ## 1、原子性 原子性在多執行緒的時候學習過,通常表示不可再分的操作,表示**事務是應用中最小的執行單位**。 ## 2、一致性 **事務操作前後,資料總量不變**。如A像B轉賬500,A得減少500,B得加上500,這樣才算是保證了資料庫的一致性。如果A減了,B沒加上去,這不是耍流氓莫。 一致性是通過原子性來保證的。 ## 3、隔離性 各個事務的執行**互不干擾**,任意一個事務的內部操作對其他併發的事務都是隔離的。 ## 4、持續性 當事務提交或回滾後,資料庫將會**持久化地儲存資料**。 # 三、事務語句 資料庫的語句由下列語句組成: - 一組DML語句。 - 一條DDL語句。 - 一條DCL語句。 模擬轉賬: ```sql CREATE TABLE account( id INT PRIMARY KEY AUTO_INCREMENT, NAME VARCHAR(10), balance DOUBLE ); INSERT INTO account (NAME,balance) VALUES ('張三',1000),('李四',1000); ``` 轉賬成功後,張三餘額還剩500,李四餘額還剩1500。 ```sql -- 張三向李四轉賬500 UPDATE account SET balance = balance - 500 WHERE NAME = '張三'; UPDATE account SET balance = balance + 500 WHERE NAME = '李四'; ``` 假設此時,在張三轉賬完畢,餘額減去500之後,資料庫出現了意料之外的異常,導致李四的餘額並沒有加上500,那麼這個問題就非常嚴重遼,就像下面這樣: ```sql UPDATE account SET balance = balance - 500 WHERE NAME = '張三'; Something IS wrong... -- 不是sql語句,將發生異常 UPDATE account SET balance = balance + 500 WHERE NAME = '李四'; ``` 就像是什麼問題呢,就像是充話費,你充了100塊錢,充完之後你一看,沒充上,血虧。 這時就可以利用事務來處理這個問題: ## 1、開啟事務:start transaction ```sql -- 開啟事務 START TRANSACTION; UPDATE account SET balance = balance - 500 WHERE NAME = '張三'; Something IS wrong... UPDATE account SET balance = balance + 500 WHERE NAME = '李四'; ``` 這時轉賬還是沒有正常執行對吧,通過`select*from account;`檢視一下,果然還是一個500,一個1000,但是其實這只是**臨時的資料,並沒有將資料永久修改**,可以再另外一個視窗檢視,發現數據並沒有變化,也就是說,當你發現臨時資料不符合預期,就可以立即進行事務回滾。 ## 2、事務回滾:rollback ```sql -- 開啟事務 START TRANSACTION; UPDATE account SET balance = balance - 500 WHERE NAME = '張三'; Something IS wrong... UPDATE account SET balance = balance + 500 WHERE NAME = '李四'; -- 回滾事務 ROLLBACK; ``` 回滾之後,一切又回到最初的起點,記憶中兩人的餘額都是1000。 ### 指定回滾點 相當於設定了個斷點:`savepoint a;`。 回滾到該斷點:`rollback to a;` ## 3、提交任務:commit 既然是臨時資料,那麼如何將他變成永久性的呢,這便是提交任務。 ```sql -- 開啟事務 START TRANSACTION; UPDATE account SET balance = balance - 500 WHERE NAME = '張三'; UPDATE account SET balance = balance + 500 WHERE NAME = '李四'; -- 提交任務 COMMIT; ``` ok,提交之後,表中資料就真正地被修改遼。 # 四、事務的提交 當事務所包含的任意一個數據庫操作執行成功或者失敗之後,都應該提交事務,無論是commit還是rollback。提交方式有兩種,自動提交或者手動提交。 ## 1、查詢事務提交方式 ```sql SELECT @@autocommit; -- 1代表自動提交,0代表手動提交 ``` **MySQL中事務提交方式預設是自動提交的。** ## 2、修改事務提交方式 ```sql -- 關閉預設提交,即開啟事務。 SET @@autocommit = 0; ``` 需要注意的是,**一旦設定將提交方式設定成手動提交,相當於開啟了一次事務**,那麼所有的DML語句都需要顯示地使用commit提交事務,或者使用rollback回滾結束事務。 > 當前會話視窗修改事務提交的方式,對其他的會話視窗沒有影響。 # 五、事務的隔離級別 **事務具有隔離性**,多個事務之間相互獨立。但多個事務操作同一批資料,將會引發一些問題,可**設定不同的隔離級別解決問題**。 ## 1、存在問題 - 髒讀(Dirty Read):一個事務讀取到另一個事務中沒有提交的資料。 - 不可重複讀(Nonrepeatale Read):在同一個事務中,兩次讀取到的資料不同。 - 幻讀(Phantom Read):一個事務操作DML資料表中所有記錄,另一個事務添加了一條資料,則第一個事務查詢不到自己的修改。 ## 2、查詢與設定隔離級別 查詢隔離級別 ```sql -- 資料庫查詢隔離級別 MySQL預設 repeatable read SELECT @@tx_isolation; ``` 設定隔離級別 ```sql -- 設定隔離級別為read-uncommited(需重啟生效) SET GLOBAL TRANSACTION ISOLATION LEVEL 隔離級別字串; ``` ## 3、隔離級別分類 ### read uncommitted:讀未提交 > 髒讀、不可重複讀、幻讀都會發生 演示 ```sql -- 設定隔離級別為read-uncommited(需重啟生效) SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; ``` 其中事務A開啟並轉賬,但是並沒有提交,此時臨時讀取到的資料是500,1500: ```sql -- 開啟事務 START TRANSACTION; UPDATE account SET balance = balance - 500 WHERE NAME = '張三'; UPDATE account SET balance = balance + 500 WHERE NAME = '李四'; ``` 這時事務B開啟並查詢賬戶,讀取到了剛才事務A並未提交掉的資料,500,1500: ```sql -- 開啟事務 START TRANSACTION; SELECT * FROM account; ``` 這時就出現了髒讀的情況,出現了虛晃,事務B以為事務A轉賬成功,其實並沒有。 此時,如果A很雞賊,將事務進行回滾rollback,雙方的資料又回到最初的起點,兩個1000,1000。 很明顯,在同一個事務中讀取到了不同的資料,也就是出現了不可重複讀的問題。 ### read committed:讀已提交 > 不可重複讀、幻讀會發生 read committed可以解決髒讀,也就是說,如果事務A沒有提交事務,事務B讀取的資料還是原來的資料,只有事務A提交事務了commit,事務B讀取到的資料才會改變。 但此時,事務B在同一事務中讀取到的兩次資料顯然又是不同的,因此不可重複讀的問題依舊存在。 ### repeatable read:可重複度 > 幻讀會發生 repeatable read是MySQL的 預設事務隔離級別,確保同一個事務的多個例項在併發讀取資料時,看到同樣的資料行,解決了不可重複讀和髒讀的問題。但是幻讀現象仍然存在: 舉另外一個例子,現在事務A開啟並查詢一個叫王五的人,顯然是查不到的。 ```sql -- 開啟事務 START TRANSACTION; SELECT * FROM account WHERE NAME="王五"; ``` 此時事務B開啟,並插入王五這個人,並提交: ```sql START TRANSACTION; INSERT INTO account (NAME,balance) VALUES ("王五",1000); COMMIT; ``` 這時事務A仍然認為表中確實沒有王五這個人,也想往裡面新增。 ```sql INSERT * INTO account (NAME,balance) VALUES ("王五",1000); ``` 這時,事務A將無法插入這條資料,因為事務B已經插入了這條資料,但是在事務A這就跟撞見了鬼一樣,什麼情況!明明搜了一下沒有這條資料,卻怎麼也插不進去!就像產生了幻覺。 這就是幻讀。 參考:[https://www.cnblogs.com/boboooo/p/12370770.html](https://www.cnblogs.com/boboooo/p/12370770.html) ### serializable:序列化 > 解決所有的問題 事務的最高級別,在每個讀的資料行上加上鎖,強制事務排序,使之不可能相互衝突,從而解決了幻讀問題。但是,將會導致大量的超時現象和鎖競爭,有點類似於執行緒中的同步鎖,很安全但效率較低。 參考:《瘋狂Java講義》