深入了解MySQL中的事務、4大特性、隔離級別

本篇文章是mysql的進階學習,給大家詳細介紹一下mysql中的事務、4大特性(acid)以及事務的隔離級別,希望對大家有所幫助!

深入了解MySQL中的事務、4大特性、隔離級別

【相關推薦:mysql視頻教程

本文操作和測試所用的環境版本是5.7.21

mysql>?select?version(); +-----------+ |?version()?| +-----------+ |?5.7.21????| +-----------+ 1?row?in?set?(0.00?sec)

記住:我們常見的MySQL存儲引擎中只有InnoDB是支持事務的。所以下面的操作也都是在InnoDB下做的。

一. 什么是事務

事務就是現實中抽象出來一種邏輯操作,要么都執行,要么都不執行,不能存在部分執行的情況。

比較經典的案例就是銀行轉賬:小A向小B轉賬100元

正常的情況:小A的賬戶扣減100元,小B的賬戶增加100元。

非正常情況: 小A的賬戶扣減100元,小B賬戶金額不變。

非正常情況下,小A賬戶扣減100之后銀行系統出現問題,小B賬戶增加100元的操作并沒有執行。也就是兩邊金額對不上了,小A不愿意,小B不愿意,銀行也不愿意啊。事務的出現就是為了避免非正常情況的出現,讓大家都滿意。

二. 事務的4大特性(ACID)

1. 原子性(Atomicity)

事務的操作是不可分割的,要么都操作,要么都不操作,就像轉賬一樣,不存在中間狀態。而且這個原子性不是說只有一個動作,可能會有很多的操作,但是從結果上看是不可分割的,也就是說原子性是一個結果狀態。

2. 一致性(Consistency)

執行事務的前后,數據保持一致,就像銀行賬戶系統一樣無論事務是否成功,兩者的賬戶總額應該是一樣的。

3. 隔離性(Isolation)

多個事務同時操作數據的時候,多個事務直接互相隔離,不會互相影響。

4. 持久性(Durability)

一個事務在提交后對數據的影響是永久的,寫入磁盤中不會丟失。

三. 顯式事務、隱式事務

mysql的事務分為顯式事務和隱式事務,默認的事務是隱式事務,由變量autocommit 在操作的時候會自動開啟,提交,回滾。

控制的關鍵命令如下

set?autocommit=0;?--?關閉自動提交事務(顯式) set?autocommit=1;?--?開啟自動提交事務(隱式) ??--?當autocommit=0的時候手動控制事務 rollback;?--?回滾事務 commit;??--?提交事務 --?當autocommit=1?自動提交事務,但是可以控制手動提交 start?transaction;?--?開啟事務(或者用begin開啟事務) commit;?--?提交事務 rollback;?--?回滾事務 SAVEPOINT?保存點名稱;??--?保存點(相當于存檔,可以不用回滾全部操作) rollback?to??保存點;??--?回滾到某個保存點?(這個后面就不測試,知道有這個操作就行)

先建一張表ajisun

mysql>?create?table?ajisun(id?int(5),?name?varchar(20)?character?set?utf8??COLLATE?utf8_bin?)?engine=innodb?character?set=?utf8mb4?COLLATE?=?utf8mb4_bin; Query?OK,?0?rows?affected?(0.03?sec)

1. 隱式事務

--?看下當前autocommit的狀態是,默認是on狀態 mysql>?show?variables?like?'autocommit';? +---------------+-------+ |?Variable_name?|?Value?| +---------------+-------+ |?autocommit????|?ON????| +---------------+-------+ 1?row?in?set?(0.01?sec)  --??插入一條數據 mysql>?insert?into?ajisun?values(1,'阿紀'); Query?OK,?1?row?affected?(0.00?sec) mysql>?rollback;  --?執行rollback?也是沒有效果的,還是能夠查詢到插入的數據(不需要我們手動控制commit) mysql>?select?*?from?ajisun; +------+--------+ |?id???|?name???| +------+--------+ |????1?|?阿紀???| +------+--------+ 1?row?in?set?(0.00?sec)

2. 顯式事務方式1

顯式事務由我們自己控制事務的開啟,提交,回滾等操作

--?開啟顯式事務-回滾 mysql>?set?autocommit=0; Query?OK,?0?rows?affected?(0.00?sec)  mysql>?select?*?from?ajisun; +------+--------+ |?id???|?name???| +------+--------+ |????1?|?阿紀???| +------+--------+ 1?row?in?set?(0.00?sec)  mysql>?insert?into?ajisun?values(2,'紀先生'); Query?OK,?1?row?affected?(0.00?sec) --?插入后可以看見2條數據 mysql>?select?*?from?ajisun; +------+-----------+ |?id???|?name??????| +------+-----------+ |????1?|?阿紀??????| |????2?|?紀先生????| +------+-----------+ 2?rows?in?set?(0.00?sec) --?回滾之后上面插入的數據就沒了 mysql>?rollback; Query?OK,?0?rows?affected?(0.00?sec)  mysql>?select?*?from?ajisun; +------+--------+ |?id???|?name???| +------+--------+ |????1?|?阿紀???| +------+--------+ 1?row?in?set?(0.00?sec)
--?插入一條數據 mysql>?insert?into?ajisun?values(2,'ajisun'); Query?OK,?1?row?affected?(0.01?sec) --?提交 mysql>?commit; Query?OK,?0?rows?affected?(0.00?sec) --?回滾 mysql>?rollback; Query?OK,?0?rows?affected?(0.00?sec) --?先提交commit,在rollback?數據依然存在,說明commit生效,事務已提交,回滾就不生效了。 mysql>?select?*?from?ajisun; +------+--------+ |?id???|?name???| +------+--------+ |????1?|?阿紀???| |????2?|?ajisun?| +------+--------+ 2?rows?in?set?(0.00?sec)

3. 顯式事務方式2

使用start transaction

先改成默認的事務 set autocommit=1;

--?開啟事務 mysql>?start?transaction; Query?OK,?0?rows?affected?(0.00?sec)  mysql>?delete?from?ajisun?where?id=1; Query?OK,?1?row?affected?(0.00?sec) --?提交事務 mysql>?commit; Query?OK,?0?rows?affected?(0.01?sec)  mysql>?select?*?from?ajisun; +------+--------+ |?id???|?name???| +------+--------+ |????2?|?ajisun?| +------+--------+ 1?row?in?set?(0.00?sec)
--?開啟事務 mysql>?start?transaction; Query?OK,?0?rows?affected?(0.00?sec)  mysql>?delete?from?ajisun?where?id?=2; Query?OK,?1?row?affected?(0.01?sec) --?回滾事務 mysql>?rollback; Query?OK,?0?rows?affected?(0.01?sec) --?刪除操作失效了 mysql>?select?*?from?ajisun; +------+--------+ |?id???|?name???| +------+--------+ |????2?|?ajisun?| +------+--------+ 1?row?in?set?(0.00?sec)

四. 并發事務中的問題

如果對表的操作同一時間只有一個事務就不會有問題,但是這是不可能的。現實中都是盡可能的利用,多個事務同時操作。多個事務就會帶來不少的問題,例如臟讀,臟寫,`不可重復讀,幻讀

1. 臟讀

一個事務讀取到另一個未提交事務修改后的數據 這就是臟讀。

例如兩個事務a,b: 同時操作一條記錄

a事務修改記錄后還沒有正式提交到數據庫,這時b事務去讀取,然后用讀取到的數據進行后續操作。

如果a事務回滾了,這個修改后的數據就不存在了,那么b事務就是在使用一個不存在的數據。這種就是臟數據。

2. 臟寫(數據丟失)

一個事務修改了另一個未提交事務修改過的數據

例如兩個事務a,b: 同時操作一條記錄

a事務修改后沒有提交, 接著b事務也修改同一條數據,然后b事務提交數據。

如果a事務回滾自己的修改,同時也把b事務的修改也回滾了,造成的問題就是:b事務修改了 也提交了,但是數據庫并沒有改變,這種情況就是臟寫。

3. 不可重復讀

一個事務只能讀到另一個已經提交的事務修改過的數據,并且其他事務每對該數據進行一次修改并提交后,該事務都能查詢得到最新值。

也就是在同一個事務中多次讀取同一條記錄,得到的內容都不一樣(在每次讀取之前都有其他事務完成修改并提交),這就是不可重復讀。

4. 幻讀

在一個事務內 相同條件查詢數據,先后查詢到的記錄數不一樣

也就是一個事務先根據某些條件查詢出一些記錄,之后另一個事務又向表中插入了符合這些條件的記錄,原先的事務再次按照該條件查詢時,能把另一個事務插入的記錄也讀出來,那就意味著發生了幻讀

不可重復讀和幻讀的區別:不可重復讀重點在于同一條記錄前后數據值不一樣(內容的變化),而幻讀重點在于相同查詢條件前后所獲取的記錄數不一樣(條數的變化)

五. 事務的隔離級別

上面說的事務的并發問題,在不同的場景下要求不一樣,能接受的問題也不一樣。他們之間的嚴重性排序如下:

臟寫 > 臟讀 > 不可重復讀 > 幻讀

MySQL中提供了4種隔離級別來處理這幾個問題,如下

隔離級別 臟讀 不可重復讀 幻影讀
READ- UNCOMMITTED
READ-COMMITTED ×
REPEATABLE-READ × ×
SERIALIZABLE × × ×

SQL 標準定義了四個隔離級別:

  • READ-UNCOMMITTED(讀未提交): 最低的隔離級別,允許讀取尚未提交的數據變更,可能會導致臟讀、不可重復讀和幻讀。但是并發度最高
  • READ-COMMITTED(讀已提交): 允許讀取并發事務已經提交的數據,可以阻止臟讀,但是幻讀和不可重復讀仍有可能發生。
  • REPEATABLE-READ(可重復讀): 對同一字段的多次讀取結果都是一致的,除非數據是被本身事務自己所修改,可以阻止臟讀和不可重復讀,但幻讀仍有可能發生。
  • SERIALIZABLE(可串行化): 最高的隔離級別,完全服從ACID的隔離級別。所有的事務依次逐個執行,這樣事務之間就完全不可能產生干擾,該級別可以防止臟讀、不可重復讀以及幻讀。并發度也是最低的
MySQL默認采用的?REPEATABLE_READ?隔離級別? Oracle默認采用的?READ_COMMITTED?隔離級別

1. 如何設置隔離級別

可以通過變量參數transaction_isolation 查看隔離級別

mysql>?SELECT?@@transaction_isolation; +-------------------------+ |?@@transaction_isolation?| +-------------------------+ |?REPEATABLE-READ?????????| +-------------------------+ 1?row?in?set?(0.00?sec)  mysql>?show?variables?like?'%transaction_isolation%'; +-----------------------+-----------------+ |?Variable_name?????????|?Value???????????| +-----------------------+-----------------+ |?transaction_isolation?|?REPEATABLE-READ?| +-----------------------+-----------------+ 1?row?in?set?(0.02?sec)

修改的命令:SET [GLOBAL|SESSION] TRANSACTION ISOLATION LEVEL $[level];

level的值就是4中隔離級別READ-UNCOMMITTED READ-COMMITTED REPEATABLE-READ SERIALIZABLE

設置全局隔離級別

只對執行完該語句之后產生的會話起作用。

當前已經存在的會話無效。

set?global?transaction_isolation='read-uncommitted'; set?global?transaction_isolation='read-committed'; set?global?transaction_isolation='repeatable-read'; set?global?transaction_isolation='serializable';

例如:

會話A

mysql>?set?global?transaction_isolation='serializable'; Query?OK,?0?rows?affected?(0.01?sec) mysql>?select?@@global.transaction_isolation; +--------------------------------+ |?@@global.transaction_isolation?| +--------------------------------+ |?SERIALIZABLE???????????????????| +--------------------------------+ 1?row?in?set?(0.00?sec) --?當前會話(設置之前就已經存在的會,級別是默認的) mysql>?select?@@transaction_isolation; +-------------------------+ |?@@transaction_isolation?| +-------------------------+ |?REPEATABLE-READ?????????| +-------------------------+ 1?row?in?set?(0.00?sec)

會話B(set之后新建的會話)

mysql>?select?@@global.transaction_isolation; +--------------------------------+ |?@@global.transaction_isolation?| +--------------------------------+ |?SERIALIZABLE???????????????????| +--------------------------------+ 1?row?in?set?(0.00?sec)  mysql>?select?@@transaction_isolation; +-------------------------+ |?@@transaction_isolation?| +-------------------------+ |?SERIALIZABLE????????????| +-------------------------+ 1?row?in?set?(0.00?sec)

設置會話的隔離級別

對當前會話的所有后續的事務有效

該語句可以在已經開啟的事務中間執行,但不會影響當前正在執行的事務。

如果在事務之間執行,則對后續的事務有效。

set?session?transaction_isolation='read-uncommitted'; set?session?transaction_isolation='read-committed'; set?session?transaction_isolation='repeatable-read'; set?session?transaction_isolation='serializable';

比如:

會話A

mysql>?set?session?transaction_isolation='read-uncommitted'; Query?OK,?0?rows?affected?(0.00?sec)  mysql>?select?@@transaction_isolation; +-------------------------+ |?@@transaction_isolation?| +-------------------------+ |?READ-UNCOMMITTED????????| +-------------------------+ 1?row?in?set?(0.00?sec)

新建會話B(依然是默認的級別:可重復讀)

mysql>?select?@@transaction_isolation; +-------------------------+ |?@@transaction_isolation?| +-------------------------+ |?REPEATABLE-READ?????????| +-------------------------+ 1?row?in?set?(0.00?sec)

2. 怎么選擇隔離級別

一般情況下默認的可重復讀就好了,一般很少改這個,除非業務場景特殊

記住一點:隔離級別越高,并發問題就越少,但并發性也就越低,所以還是要根據業務選擇來。

六. 總結

  • 事務的四大特性:原子性,一致性,隔離性,持久性

  • 事務的常見命令:

    set?autocommit=0/1;?--?關閉/開啟自動提交事務 start?transaction;?--?開啟事務(或者用begin) rollback;?--?回滾事務 commit;?--?提交事務
  • 并發事務的問題:臟寫 > 臟讀 > 不可重復讀 > 幻讀

  • 需要熟悉事務的4種隔離級別以及MySQL默認級別

  • 怎么設置隔離級別(global,session)

更多編程相關知識,請訪問:mysql視頻教程!!

? 版權聲明
THE END
喜歡就支持一下吧
點贊6 分享