在MySQL的眾多存儲引擎中,只有InnoDB支持事務,所有這里說的事務隔離級別指的是InnoDB下的事務隔離級別。
一、事務的基本要素(ACID)
- 原子性(Atomicity);事務開始后所有操作,要么全部做完,要么全部不做,不能停滯在中間環節。
- 一致性(Consistency):事務開始前和結束后,數據庫的完整性約束沒有被破壞。
- 隔離性(Isolation):同一時間,只允許一個事務請求同一數據,不同的事務之間彼此沒有任何干擾。
- 持久性(Durability):事務完成后,事務對數據庫的所有更新將被保存到數據庫,不能回滾。
二、事務的並發問題
- 臟讀:事務B修改數據但未提交,事務A讀數據,然后B回滾,則A讀到的是臟數據。
- 不可重復讀:事務A第一次讀取數據,事務B修改數據提交,事務A第二次讀數據,兩次數據不一致。
- 幻讀:事務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
