1. 程式人生 > 其它 >事務的四大特性ACID和資料庫的隔離級別

事務的四大特性ACID和資料庫的隔離級別

技術標籤:mysqlsql

事務的四大特性ACID

資料庫四大屬性原子性(Atomicity),一致性(Consistency),隔離性(Isolation),一致性(Durability)

  • 原子性。整個事務中的全部操作要麼全部完成,要麼全部都不完成。如果在事務執行的某個階段發生錯誤,則將資料庫回滾到執行事務之前。
  • 一致性。事務必須保證系統處於一致的狀態
  • 隔離性。事務之間的隔離級別
  • 永續性。在事務完成之後,該事務對資料庫所做的更改便永久儲存到資料庫中

資料庫的隔離級別

當多個事務併發執行時,可能會純在下面幾個問題

  • 髒讀:事務A讀到了事務B中尚未提交的資料

  • 不可重複讀:事務A中多次讀同一資料,讀到的結果不一樣。一般是因為在事務A執行的過程中存在事務B update了這組資料

  • 幻讀:事務A中多次查詢一組資料,查到的資料行數不一樣。一般是因為在事務A執行的過程中存在事務B 插入或者刪除了資料

為了解決這三類問題,資料庫提供四層事務的隔離級別,隔離基本由低到高依次是:

  • 讀取未提交(Read Uncommitted),存在髒讀, 不可重複讀, 幻讀的問題

  • 讀取已提交(read commit),存在不可重複讀, 幻讀的問題;能夠解決髒讀

  • 可重複讀(repeatable),存在幻讀的問題;能夠解決髒讀,不可重複讀

  • 序列化(Serializable),能夠解決髒讀,幻讀,不可重複讀

需要注意的是隔離級別越高,執行速度越慢

讀取未提交(READ UNCOMMITTED)

讀取未提交(READ UNCOMMITTED):即使事務B沒有提交,事務A也可以讀到事務B中已經執行的修改。

舉個X向Y轉賬的例子, 大致流程如下

時刻事務A事務B事務A讀到的餘額事務B讀到的餘額
T0開啟事務開啟事務X: 10000元, Y: 10000元X: 10000元, Y: 10000元
T1X向Y轉賬1000X: 9000元, Y: 11000元X: 9000元, Y: 11000元
T2提交事務X: 9000元, Y: 11000元-
T3回滾操作--

建表語句

DROP TABLE IF EXISTS account;
CREATE TABLE account (
	id INT PRIMARY KEY,
    name VARCHAR(20) not null
, balance INT ); INSERT INTO account VALUES(1, "X", 10000), (2, "Y", 10000);

開啟兩個視窗訪問同一資料庫

把兩個session的事務隔離級別修改為Read Uncommitted

SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

image-20201029114109222

在session A中開啟事務

BEGIN;

在session B中開啟事務

BEGIN;

在事務A中模擬X向Y轉賬1000元在事務A中把X的賬戶減去1000元,Y的賬戶增加1000元

UPDATE account SET balance = balance - 1000 where id = 1;
UPDATE account SET balance = balance + 1000 where id = 2;

在事務B中查詢兩個賬戶的餘額, 可以看到事務B中可以查到事務A做出的更改(這就是髒讀),此時Y可能認為轉賬已經到賬了,然後就發貨了

image-20201029144341976

提交事務B

COMMIT;

回滾事務A

ROLLBACK;

在事務B中查詢兩個賬戶的餘額, 可以X和Y的餘額又變成之前的狀態了。Y並沒有收到轉賬,由此可見,髒讀是有危害的。

image-20201029145135112

提交事務B

讀已提交(READ COMMITTED)

讀寫提交(READ COMMITTED): 事務B提交之之後,事務A才能讀到B中已經執行的修改。如果事務B沒有提交,事務A不能讀到B中已經執行的修改

舉個X向Y轉賬的例子, 大致流程如下

時刻事務A事務B事務A餘額事務B餘額
T0開啟事務開啟事務X: 10000元, Y: 10000元X: 10000元, Y: 10000元
T1X向Y轉賬1000X: 9000元, Y: 11000元X: 10000元, Y: 10000元
T2提交事務-X: 9000元, Y: 11000元
T3提交事務--

開啟兩個視窗訪問同一資料庫

把兩個session的事務隔離級別修改為READ COMMITTED

SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

在session A, session B中開啟事務

BEGIN;

在事務A中模擬X向Y轉賬1000元在事務A中把X的賬戶減去1000元,Y的賬戶增加1000元

UPDATE account SET balance = balance - 1000 where id = 1;
UPDATE account SET balance = balance + 1000 where id = 2;

在事務B中查詢兩個賬戶的餘額, 可以看到沒有讀到事務A做的修改,這樣就避免了髒讀的問題。

image-20201029150709557

提交事務A

在事務B中查詢兩個賬戶的餘額, 可以看到在提交事務A之後,事務B讀到了事務A的修改,這就是不可重複讀。

image-20201029150953684

提交事務B

不可重複讀的危害:假設事務B需要先計算X的存款的利息,然後在核算一次,但是由於事務A中X向Y轉賬了。事務B中計算到的兩次利息的值對不上,就會很奇怪

可重複讀(REPEATABLE READ)

可重複讀(REPEATABLE READ): 事務中讀到的資料都是事務開始時的值,其他事務的修改不會影響到該事務。

把賬戶X和Y的餘額從新設定為10000

舉個X向Y轉賬的例子, 大致流程如下

時刻事務A事務B事務A餘額事務B餘額
T0開啟事務開啟事務X: 10000元, Y: 10000元X: 10000元, Y: 10000元
T1X向Y轉賬1000X: 9000元, Y: 11000元X: 10000元, Y: 10000元
T2提交事務-X: 10000元, Y: 10000元
T3提交事務--

開啟兩個視窗訪問同一資料庫

把兩個session的事務隔離級別修改為REPEATABLE READ

SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;

在session A, session B中開啟事務

BEGIN;

在事務A中模擬X向Y轉賬1000元在事務A中把X的賬戶減去1000元,Y的賬戶增加1000元

UPDATE account SET balance = balance - 1000 where id = 1;
UPDATE account SET balance = balance + 1000 where id = 2;

在事務B中查詢兩個賬戶的餘額, 可以看到沒有讀到事務A做的修改,這樣就避免了髒讀的問題。

image-20201029150709557

提交事務A

在事務B中查詢兩個賬戶的餘額, 可以看到在事務A提交之後,事務B也沒有讀到事務A做的修改,這樣就避免了不可重複讀的問題。

image-20201029150709557

提交事務B

需要注意的是MYSQL中無法看到幻讀的現象。

序列化(SERIALIZABLE)

序列化(SERIALIZABLE):一旦有事務操作某個表,它就會鎖住這個表,直到提交該事務,其他事務才能修改這張表;

舉個轉賬的例子

把賬戶X和Y的餘額從新設定為10000

開啟兩個視窗訪問同一資料庫

把兩個session的事務隔離級別修改為SERIALIZABLE

SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;

開啟兩個視窗訪問同一資料庫

把兩個session的事務隔離級別修改為REPEATABLE READ

在事務B中查詢兩個賬戶的餘額

image-20201029161438697

在事務A中模擬X向Y轉賬1000元在事務A中把X的賬戶減去1000元,Y的賬戶增加1000元,發現會卡住

UPDATE account SET balance = balance - 1000 where id = 1;
UPDATE account SET balance = balance + 1000 where id = 2;

image-20201029161659262

提交事務B;

在事務A再次模擬X向Y轉賬1000元在事務A中把X的賬戶減去1000元

提交事務A

再次查詢餘額

UPDATE account SET balance = balance - 1000 where id = 1;
UPDATE account SET balance = balance + 1000 where id = 2;

image-20201029161817783