【MySQL(3)| 五分鐘搞清楚MySQL事務隔離級別】

事務

什麼是事務?數據庫

數據庫操做的最小工做單元,是做爲單個邏輯工做單元執行的一系列操做;事務是一組不可再分割的操做集合(工做邏輯單元)編程

舉個栗子:bash

事務最經典經常使用的栗子可能就是轉帳:一個帳戶少錢了,哪另外一個帳戶確定要多錢,李永龍說過,虧本的買賣咱可不幹,吃虧了不高興!微信

因此,少錢和多錢這兩個操做,要麼同時成功,要麼同時失敗!session

MySQL中如何開啓事務?併發

  • 手工開啓:begin/start transaction性能

  • 事務提交或回滾:commit/rollback學習

  • 設定事務是否自動開啓:set session autocommit = on/off測試

在JDBC中或者Spring事務AOP編程中均可以進行上述設置,你們能夠查閱相關的文檔,本文不在贅述。ui

事務ACID特性

  • 原子性(Atomicity

    最小的工做單元,要麼一塊兒成功,要麼一塊兒失敗

  • 一致性(Consistency)

    一致性也稱做是完整性,就是說事務的執行不能破壞數據庫的一致性,一個事務在執行後,數據庫必須從一個狀態轉變爲另外一個狀態

  • 隔離性(Isolation)

    併發的事務相互隔離,互不干擾

  • 持久性(Durability)

    持久性是指事務一旦提交,對數據庫的狀態就應該被永久保存

事務併發帶來了哪些問題?

  • 髒讀

髒讀就是指當一個事務正在訪問數據,而且對數據進行了修改,而這種修改尚未提交到數據庫中,這時,另一個事務也訪問這個數據,而後使用了這個數據。

舉個栗子:

1.張三這我的工資是10000,財務將張三的工資改成了20000(可是事務未提交);

2.張三讀取本身工資的時候,發現工資變成20000,很是開心;

3.而後財務發現操做有誤,本身改錯了,隨之回滾了事務,張三的工資又變成了10000,結果小夥那叫個傷心欲絕啊。

以上:張三讀取到的這個20000就是個髒數據

  • 幻讀

是指當事務不是獨立執行時發生的一種現象,例如第一個事務對一個表中的數據進行了修改,這種修改涉及到表中的所有數據行,同時,第二個事務也修改這個表中的數據,這種修改是向表中插入一行新數據。那麼,之後就會發生操做第一個事務的用戶發現表中還有沒有修改的數據行,就好象發生了幻覺同樣。

舉個栗子:

假設某公司工資爲10000的有10我的

1.事務1,讀取全部工資爲10000的員工,會讀取到10條記錄;

2.事務2此時向工資表中插入了一條員工記錄,工資正好也是10000;

3.事務1再次讀物全部工資爲10000的員工,會發現讀到了11條記錄。

以上:就產生了幻覺,若是咱們能作到在操做事務未完成數據處理以前,其餘的任何事務都不能夠添加新數據,則能避免該問題的發生。

  • 不可重複讀

是指在一個事務內,屢次讀同一數據。在這個事務尚未結束時,另一個事務也訪問該同一數據。那麼,在第一個事務中的兩次讀數據之間,因爲第二個事務的修改,那麼第一個事務兩次讀到的的數據多是不同的。這樣在一個事務內兩次讀到的數據是不同的,所以稱爲是不可重複讀。

舉個栗子:

1.在事務1中,張三 讀取了本身的工資爲10000,可是操做尚未完成;

2.在事務2中,正好財務人員修改了張三的工資爲20000,並提交了事務;

3.在事務1中,張三再次讀取本身的工資時,工資變爲了20000。

以上:此時至關於發生了不可重複讀,若是隻有在修改事務徹底提交以後才能夠讀取數據,則能夠避免該問題。

不可重複讀的重點是修改 :
一樣的條件 , 你讀取過的數據 , 再次讀取出來發現值不同了
幻讀的重點在於新增或者刪除
一樣的條件 , 第 1 次和第 2 次讀出來的記錄數不同

爲了解決上邊所說的幾個問題,下面介紹下MySQL對於事務處理的四種隔離級別

事務四種隔離級別

  • Read uncommitter(未提交讀) : 沒有解決任何問題

  • Read Committer(提交讀) :解決了髒讀問題

  • Repeatable Read(可重複讀): 解決了不可重複讀和髒讀問題(ps:在Innodb狀況下,也不可能發生幻讀問題)

  • Serializable(串行化) :髒讀、幻讀、不可重複讀三個問題所有解決了

爲了更好的介紹以上四種狀況,再舉個栗子:

建一張表並插入兩條數據:

-- 建表語句
CREATE TABLE `t_transaction` (
  `id` INT(5) NOT NULL AUTO_INCREMENT,
  `account` INT(5) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPACT COMMENT='事務隔離級別測試表';
-- 插入數據
INSERT INTO t_transaction (id,account) VALUES(1,1000);
INSERT INTO t_transaction (id,account) VALUES(2,1000);
複製代碼

表結構以下:


打開兩個會話,準備模擬環境。

Read uncommitter

而後在會話1執行以下語句:

SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
START TRANSACTION;
SELECT * FROM t_transaction;
複製代碼

執行結果以下:

img

接下來在會話2執行以下語句,把id爲1的記錄 account 值增長 200,可是並沒有提交事務

SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
START TRANSACTION;
UPDATE t_transaction SET account = account+200 WHERE id = 1;
複製代碼

回過頭咱們再在會話1執行一次查詢:

SELECT * FROM t_transaction;
複製代碼

執行結果以下:

結論

咱們將事務隔離級別設置爲read uncommitted,即使是事務沒有commit,在其餘會話或者說事務中咱們仍然能讀到未提交的數據,這是全部隔離級別中最低的一種,這種狀況屬於髒讀

這樣作的話就會產生一個問題:

那就是咱們在一個事務中能夠隨隨便便讀取到其餘事務未提交的數據,這仍是比較麻煩的,咱們叫髒讀。我不知道這個名字是怎麼起的,爲了加強你們的印象,能夠這麼想,這個事務好輕浮啊,飢渴到連別人沒提交的東西都等不及,真髒,呸!

實際上咱們的數據改變了嗎?

答案是否認的,由於只有事務commit後纔會真正更新到數據庫。

Read committed

咱們利用以下語句將會話2的事務隔離級別設置爲read committed

SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
複製代碼

而後在會話1中執行以下語句:

update t_transaction set account=account-200 where id=1;
複製代碼

咱們將id=1的用戶account減200。而後查詢,發現id=1的用戶account變爲800。


在會話2中查詢:

select * from t_transaction;
複製代碼



咱們會發現數據並無變,仍是1000。

接着在會話A中咱們將事務提交:commit

緊接着在會話2中查看結果以下:


結論

當咱們將當前會話的隔離級別設置爲read committed的時候,當前會話只能讀取到其餘事務提交的數據,未提交的數據讀不到。

咱們在會話2同一個事務中,讀取到兩次不一樣的結果。這就形成了不可重複讀,就是兩次讀取的結果不一樣,這種現象稱之爲不可重複讀

RepeatableRead

這是MySQL默認的隔離級別

在會話2中咱們當前事務隔離級別爲repeatable read。具體操做以下:

set session transaction isolation level repeatable read;
start transaction;
複製代碼

接着在會話2中查詢數據:


咱們在會話1中爲表account添加一條數據:

insert into t_transaction(id,account) value(3,1000);
commit;
複製代碼

而後咱們查詢看數據插入是否成功:


回到會話2,咱們查詢結果:


會話2中想插入一條新數據id=3,value=1000。來咱們操做下:

insert into t_transaction(id,account) value(3,1000);
複製代碼


執行結果:

結果,問題產生了,居然插不進去

結論

此處須要注意的是,由於咱們使用的Innodb引擎,因此此處不會產生幻讀,其餘引擎的話在這個隔離級別可能會產生幻讀(至於爲何說Innodb不會,且聽下回分解,其實MySQL 利用鎖機制和MVCC避免了這個問題,感興趣的同窗能夠自行查閱)

Serializable

一樣,咱們將會話2的事務隔離級別設置爲serializable並開啓事務。

set session transaction isolation level serializable;
start transaction;
複製代碼

在會話2中咱們執行下面操做:

select * from account;
複製代碼



那咱們這個時候在用戶A所在的會話中寫數據呢?

咱們發現會話1所在的會話陷入等待,若是超時(這個時間能夠進行配置),會出現Lock wait time out提示:


若是在等待期間咱們用戶B所在的會話事務提交,那麼用戶A所在的事務的寫操做將提示操做成功。

結論

當咱們將當前會話的隔離級別設置爲serializable的時候,其餘會話對該表的寫操做將被掛起。能夠看到,這是隔離級別中最嚴格的,可是這樣作勢必對性能形成影響。因此在實際的選用上,咱們要根據當前具體的狀況選用合適的隔離級別。

小結

下面用一張圖來簡單總結一下


下一篇會深刻學習Innodb引擎提供的鎖機制和MVCC機制,敬請期待!

歡迎關注微信公衆號 程序猿雜貨鋪 公號ID zhoudl_l


相關文章
相關標籤/搜索