Mysql事務
這里主要講的是Mysql InnoDB引擎相關事務和鎖。Mysql事務主要和上訴數據庫理論中類似,有所不同的是在事務隔離級別的Repeatable Read(可重復讀)、和鎖有着不同的實現。
事務隔離級別
Mysql同樣有4種事務隔離級別,其中Repeatable Read(可重復讀)是Mysql默認隔離級別,其通過MVCC機制實現了不會出現幻讀現象。
隔離級別 | 臟讀 | 不可重復讀 | 幻讀 |
---|---|---|---|
Read Uncommitted | 可能 | 可能 | 可能 |
Read Committed | 不能 | 可能 | 可能 |
Repeatable Read | 不能 | 不能 | 不能 |
Serializable | 不能 | 不能 | 不能 |
Mysql可以通過下訴API操作事務隔離級別:
查看系統隔離級別:
select @@global.tx_isolation;
查看當前會話隔離級別
select @@tx_isolation;
設置當前會話隔離級別
SET session TRANSACTION ISOLATION LEVEL serializable;
設置全局系統隔離級別
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
Mysql鎖
Mysql InnoDB引擎有悲觀鎖、樂觀鎖(MVCC),其共同組成Mysql相關鎖機制,並且Mysql通過其使用Repeatable Read能夠防止幻讀的出現。
Mysql悲觀鎖
Mysql主要有下面4種悲觀鎖:
- 共享鎖(S鎖、多鎖): 事務獲得元組的共享鎖后,其它事務也只能獲得該元組的共享鎖,而不能獲得排它鎖;獲得共享鎖的事務可以對元組進行讀操作。
- 排它鎖(X鎖,寫鎖): 事務獲得元組的排它鎖后,其它事務既不能獲得該元組的共享鎖,也不能獲得排它鎖;獲得排它鎖的事務可以對元組進行寫操作。
- 意向共享鎖(IS鎖): 事務打算給數據行加行共享鎖,事務在給一個數據行加共享鎖前必須先取得該表的IS鎖。
- 意向排它鎖(IX鎖): 事務打算給數據行加行排他鎖,事務在給一個數據行加排他鎖前必須先取得該表的IX鎖。
IS、S、IX、X鎖的兼容性為:
Mysql InnoDB引擎默認支持行鎖,來盡量縮小鎖定元組的粒度,行鎖分為三級,粒度從小到大依次是:
- 記錄鎖(Record Lock):單行
- 間隔鎖(Gap Lock):一個開區間內的多行
- 防插入鎖(Next-Key Lock):一個前開后閉區間內的多行,實際上是記錄鎖和間隔鎖的結合
Mysql樂觀鎖(MVCC)
前面所說,樂觀鎖機制通過在表中添加version
字段進行實現。在Mysql InnoDB引擎中,其會在每行數據中額外添加兩個隱藏值來實現MVCC,這兩個值一個記錄這行數據何時被創建,另外一個記錄這行數據何時更新(或者被刪除)。在實際操作中,每開啟一個新事務,事務的版本號就會遞增。
Mysql事務隔離級別和其對應鎖級別
Read Uncommited
任何操作都不加鎖
Read Commited
操作 | 鎖對應級別 |
---|---|
select * | 無鎖 |
select * for update | 排它鎖 |
select * lock in share mode | 共享鎖 |
insert | 排它鎖,行鎖中的記錄鎖 |
delete | 排它鎖,行鎖中的記錄鎖 |
update | 排它鎖,行鎖中的記錄鎖 |
Repeated Read
操作 | 鎖對應級別 |
---|---|
select * | 樂觀鎖(MVCC機制),也叫快照讀(snapshot read) |
select * for update | 排它鎖 |
select * lock in share mode | 共享鎖 |
insert | 排它鎖,行鎖中的記錄鎖 |
delete | 排它鎖,行鎖中的防插入鎖 |
update | 排它鎖,行鎖中的防插入鎖 |
Serializable
該隔離級別下,讀(select)下會加共享鎖,相當於select lock in share mode,寫會加排他鎖,讀寫互斥,相當於全部使用悲觀鎖實現事務隔離級別。
Mysql解決Repeatable Read事務隔離級別的可重復讀和幻讀問題
Mysql在Repeatable Read事務隔離級別下,通過MVCC機制、update和delete操作的防插入鎖解決可重復讀和幻讀問題。
可重復讀問題
這里主要由Mysql的MVCC機制所解決的。mysql會在每行數據中額外添加何時被創建
、何時被更新
兩個版本號字段,在RP級別下,每個操作對應得MVCC版本號字段的操作:
- select: 讀取創建版本號<=當前版本號,更新版本號為空或<=當前版本號
- insert: 保存當前事務版本號為行的創建版本號
- update: 保存當前事務版本號為行創建版本號,同時保存當前事務版本號到行更新版本號
- delete: 保存當前事務版本號為行的更新版本號
所以我們由上可知在事務A在select操作時,事務B的update和delete操作並不會影響事務A的select操作。
幻讀問題
幻讀問題我們主要是由於insert操作導致,所以這里面主要涉及到select時有insert操作和update(delete)時有insert操作,下面主要講這兩種Mysql InnoDB引擎是如何解決的。
select時的insert操作
由上所訴解決可重復讀問題,該操作已由Mysql的MVCC機制所解決
update和delete時的insert操作
該操作主要由Mysql InnoDB引擎的防插入鎖(Next-Key Lock)解決的。防插入鎖的區間是根據索引來確定的。對於沒有索引的列會直接鎖表。下面是一個例子:
--------------------------------------------------------------------------------------------------
事務A | 事務B
--------------------------------------------------------------------------------------------------
begin | begin
--------------------------------------------------------------------------------------------------
update isolation set name = '-2' where name = '2'; |
--------------------------------------------------------------------------------------------------
| insert into isolation(name) values('2');
| // 這里會阻塞等待
--------------------------------------------------------------------------------------------------------
commit |
--------------------------------------------------------------------------------------------------------
注意:上面事務A的update操作會造成name='2'的行鎖和name=(-∞, 2]和[2, +∞)的間隔鎖,其統稱為防插入鎖。
Reference
https://tech.meituan.com/innodb-lock.html
http://chenzhou123520.iteye.com/blog/1860954
https://blog.csdn.net/sadfishsc/article/details/51027734