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


一、事務的概念

什么是事務呢?

事務是由一步或幾步數據庫操作序列組成的邏輯執行單元,這系列操作要么全部執行,要么全部放棄執行

二、事務的四大特性

原子性(Atomic),一致性(Consistency),隔離性(Isolation),持續性(Durability),簡稱ACID性。

1、原子性

原子性在多線程的時候學習過,通常表示不可再分的操作,表示事務是應用中最小的執行單位

2、一致性

事務操作前后,數據總量不變。如A像B轉賬500,A得減少500,B得加上500,這樣才算是保證了數據庫的一致性。如果A減了,B沒加上去,這不是耍流氓莫。

一致性是通過原子性來保證的。

3、隔離性

各個事務的執行互不干擾,任意一個事務的內部操作對其他並發的事務都是隔離的。

4、持續性

當事務提交或回滾后,數據庫將會持久化地保存數據

三、事務語句

數據庫的語句由下列語句組成:

  • 一組DML語句。
  • 一條DDL語句。
  • 一條DCL語句。

模擬轉賬:

CREATE TABLE account(
	id INT PRIMARY KEY AUTO_INCREMENT,
	NAME VARCHAR(10),
	balance DOUBLE
	);
	
INSERT INTO account (NAME,balance) VALUES ('張三',1000),('李四',1000);

轉賬成功后,張三余額還剩500,李四余額還剩1500。

-- 張三向李四轉賬500
UPDATE account SET balance = balance - 500 WHERE NAME = '張三';
UPDATE account SET balance = balance + 500 WHERE NAME = '李四';

假設此時,在張三轉賬完畢,余額減去500之后,數據庫出現了意料之外的異常,導致李四的余額並沒有加上500,那么這個問題就非常嚴重遼,就像下面這樣:

UPDATE account SET balance = balance - 500 WHERE NAME = '張三';
Something IS wrong...	-- 不是sql語句,將發生異常
UPDATE account SET balance = balance + 500 WHERE NAME = '李四';

就像是什么問題呢,就像是充話費,你充了100塊錢,充完之后你一看,沒充上,血虧。

這時就可以利用事務來處理這個問題:

1、開啟事務:start transaction

-- 開啟事務
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

-- 開啟事務
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

既然是臨時數據,那么如何將他變成永久性的呢,這便是提交任務。

-- 開啟事務
START TRANSACTION;

UPDATE account SET balance = balance - 500 WHERE NAME = '張三';
UPDATE account SET balance = balance + 500 WHERE NAME = '李四';

-- 提交任務
COMMIT;

ok,提交之后,表中數據就真正地被修改遼。

四、事務的提交

當事務所包含的任意一個數據庫操作執行成功或者失敗之后,都應該提交事務,無論是commit還是rollback。提交方式有兩種,自動提交或者手動提交。

1、查詢事務提交方式

SELECT @@autocommit; -- 1代表自動提交,0代表手動提交

MySQL中事務提交方式默認是自動提交的。

2、修改事務提交方式

-- 關閉默認提交,即開啟事務。
SET @@autocommit = 0;

需要注意的是,一旦設置將提交方式設置成手動提交,相當於開啟了一次事務,那么所有的DML語句都需要顯示地使用commit提交事務,或者使用rollback回滾結束事務。

當前會話窗口修改事務提交的方式,對其他的會話窗口沒有影響。

五、事務的隔離級別

事務具有隔離性,多個事務之間相互獨立。但多個事務操作同一批數據,將會引發一些問題,可設置不同的隔離級別解決問題

1、存在問題

  • 臟讀(Dirty Read):一個事務讀取到另一個事務中沒有提交的數據。
  • 不可重復讀(Nonrepeatale Read):在同一個事務中,兩次讀取到的數據不同。
  • 幻讀(Phantom Read):一個事務操作DML數據表中所有記錄,另一個事務添加了一條數據,則第一個事務查詢不到自己的修改。

2、查詢與設置隔離級別

查詢隔離級別

-- 數據庫查詢隔離級別 MySQL默認 repeatable read
SELECT @@tx_isolation;

設置隔離級別

-- 設置隔離級別為read-uncommited(需重啟生效)
SET GLOBAL TRANSACTION ISOLATION LEVEL 隔離級別字符串;

3、隔離級別分類

read uncommitted:讀未提交

臟讀、不可重復讀、幻讀都會發生

演示

-- 設置隔離級別為read-uncommited(需重啟生效)
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

其中事務A開啟並轉賬,但是並沒有提交,此時臨時讀取到的數據是500,1500:

-- 開啟事務
START TRANSACTION;
UPDATE account SET balance = balance - 500 WHERE NAME = '張三';
UPDATE account SET balance = balance + 500 WHERE NAME = '李四';

這時事務B開啟並查詢賬戶,讀取到了剛才事務A並未提交掉的數據,500,1500:

-- 開啟事務
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開啟並查詢一個叫王五的人,顯然是查不到的。

-- 開啟事務
START TRANSACTION;

SELECT * FROM account WHERE NAME="王五";

此時事務B開啟,並插入王五這個人,並提交:

START TRANSACTION;
INSERT INTO account (NAME,balance) VALUES ("王五",1000);
COMMIT;

這時事務A仍然認為表中確實沒有王五這個人,也想往里面添加。

INSERT * INTO account (NAME,balance) VALUES ("王五",1000);

這時,事務A將無法插入這條數據,因為事務B已經插入了這條數據,但是在事務A這就跟撞見了鬼一樣,什么情況!明明搜了一下沒有這條數據,卻怎么也插不進去!就像產生了幻覺。

這就是幻讀。

參考:https://www.cnblogs.com/boboooo/p/12370770.html

serializable:串行化

解決所有的問題

事務的最高級別,在每個讀的數據行上加上鎖,強制事務排序,使之不可能相互沖突,從而解決了幻讀問題。但是,將會導致大量的超時現象和鎖競爭,有點類似於線程中的同步鎖,很安全但效率較低。

參考:《瘋狂Java講義》


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM