Mysql 事務隔離級別


在MySQL的眾多存儲引擎中,只有InnoDB支持事務,所有這里說的事務隔離級別指的是InnoDB下的事務隔離級別。

一、事務的基本要素(ACID)

  1. 原子性(Atomicity);事務開始后所有操作,要么全部做完,要么全部不做,不能停滯在中間環節。
  2. 一致性(Consistency):事務開始前和結束后,數據庫的完整性約束沒有被破壞。
  3. 隔離性(Isolation):同一時間,只允許一個事務請求同一數據,不同的事務之間彼此沒有任何干擾。
  4. 持久性(Durability):事務完成后,事務對數據庫的所有更新將被保存到數據庫,不能回滾。

二、事務的並發問題

  1. 臟讀:事務B修改數據但未提交,事務A讀數據,然后B回滾,則A讀到的是臟數據。
  2. 不可重復讀:事務A第一次讀取數據,事務B修改數據提交,事務A第二次讀數據,兩次數據不一致。
  3. 幻讀:事務A update表的全部行,事務B插入一行,事務A就會發現表中還有未修改的行。(一般加間隙鎖)

三、MySQL事務隔離級別

事務隔離級別 臟讀 不可重復讀 幻讀
讀未提交 會 
讀已提交 不會
可重復讀 不會 不會
串行話 不會 不會 不會

查看mysql的默認事務隔離級別“show global variables like ‘tx_isolation’; ”

數據庫的鎖是加在數據行對應的索引上的

 =======================悲觀鎖方向=========================================

四、InnoDB 行鎖模式

Innodb的行鎖模式有以下幾種:共享鎖,排他鎖,意向共享鎖(表鎖),意向排他鎖(表鎖),間隙鎖。

1.共享鎖

又稱讀鎖,讀取操作創建的鎖。一旦上鎖,任何事物無法對其修改,但可以並發讀取數據,也可以對此數據再加共享鎖。

2.排他鎖

又稱寫鎖,如果事務對數據A加上排他鎖后,則其他事物不可並發讀取數據,也不能再對A加任何類型的鎖。獲准排他鎖的事務既能讀取數據,又能修改數據。

在MySQL InnoDB中,UPDATE/INSERT/DELETE操作都會自動加排他鎖,普通的SELECT語句不會加任何鎖,如果想加鎖,可以使用下面方式

SELECT * FROM table_name WHERE id = 1 LOCK IN SHARE MODE; -- 顯式加共享鎖
SELECT * FROM table_name WHERE id = 1 FOR UPDATE;         -- 顯式加排他鎖

3.意向鎖 (數據庫自動幫加)

InnoDB為了讓表鎖和行鎖共存而使用了意向鎖。(意向鎖均為表級鎖)

事務A既然鎖住了某一行,其他事務就不可能修改這一行。這與”事務B鎖住整個表就能修改表中的任意一行“形成了沖突。所以,沒有意向鎖的時候,行鎖與表鎖共存就會存在問題!

意向共享鎖:表示事務准備給數據行加入共享鎖(行讀鎖S), 先加意向共享鎖IS

意向排他鎖:事務准備給數據行加入排他鎖(行寫鎖X),先加意向排他鎖IX

事務A在申請行寫鎖之前,數據庫會自動給事務A申請表的意向排他鎖,當事務B申請表寫鎖時,因為表上已經有意向排他鎖,所以B申請的寫鎖會被阻塞。

 意向鎖相互兼容,因為IX、IS只是表明申請更低層次級別元素(比如 page、記錄)的X、S操作

 4.記錄鎖、間隙鎖、臨鍵鎖、意向插入鎖

=================================樂觀部分===============================

MVCC

Multi-Version Concurrency Control 即多版本並發控制,是為了解決讀-寫沖突而出現的。

在MySQL中,多版本並發控制是InnoDB存儲引擎實現讀已提交和讀未提交,隔離級別的具體方式。讀未提交總是讀取最新數據行,無需使用MVCC;可串行化需要對所有讀取的行都加鎖,單純使用MVCC無法實現。

MVCC一般用兩種實現方式,InnoDB采用的是后者

  • 實時保留數據的一個或者多個歷史版本
  • 在需要時通過undo日志構造出歷史版本

InnoDB為每個記錄行都實現了三個隱藏字段

6字節的事務ID(DATA_TRX_ID)標記了最新更新這條記錄的事務id

7字節的回滾指針  (RAR_ROLL_PTR)指向當前及錄像的rollback segment 的undo log記錄,找之前版本的數據就是通過這個指針

一個6字節的DB_ROW_ID字段包含一個行ID,當插入新行時,該行ID會單調增加。如果 InnoDB自動生成聚簇索引,索引包含行ID值。否則,該 DB_ROW_ID列不會出現在任何索引中。

另外,每條記錄的頭信息(record header)里都有一個專門的bit(deleted_flag來表示當前記錄是否已經被刪除

 

 

更新字段時會進行如下操作

用排他鎖鎖定該行

記錄redo log

把該行修改前的值復制到undolog,即上圖中下面的行。。

修改當前行的值,填寫事務編號,回滾指針指向undo log剛剛copy的行

select 

  • InnoDB只查找版本號小於等於當前事務版本的數據行,這樣可以確保數據行要么是在開始之前已經存在 了,要么是本事務自身插入或修改過的
  • 行的刪除版本號 要么未定義,要么大於當前事務版本號,這樣可以確保事務讀取到的行,在事務開始之前未被刪除。

insert  InnoDB為新插入的每一行保存當前事務版本號作為事務ID

delete  InnoDB為刪除的每一行保存當前事務版本號作為事務ID

update InnoDB為插入一行新紀錄,保存當前系統版本號作為行版本號, 同事保存當前系統版本號到原來的行作為行刪除標識符

 

對於READ_COMMITTED 

讀提交是,讀事務每次都讀取undo log中最近的版本,因此每次都能讀取到最新的數據。

MySQL的讀一致性,是通過一個叫read view的結果來實現的。

readview中維護了系統中活躍事務集合的快照。詳見https://yq.aliyun.com/articles/560506


免責聲明!

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



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