提到事務,你確定會想到ACID(Atomicity、Consistency、Isolation、Durability,即原子性、一致性、隔離性、持久性),咱們就來講說其中I,也就是「隔離性」。html
當數據庫上有多個事務同時執行的時候,就可能出現髒讀(dirty read)、不可重複讀(non-repeatable read)、幻讀(phantom read)的問題,因此下面咱們來講說隔離級別。mysql
SQL標準的事務隔離級別包括:讀未提交(read uncommitted)、讀提交(read committed)、可重複讀(repeatable read)、串行化(serializable)。sql
MySQL雖然支持4種隔離級別,但與SQL標準中所規定的各級隔離級別容許發生的問題卻有些出入,MySQL在REPEATABLE READ隔離級別下,是能夠禁止幻讀問題的發生的。數據庫
咱們能夠經過: SET [GLOBAL|SESSION] TRANSACTION ISOLATION LEVEL level;
來設置隔離級別。bash
其中的level可選值有4個:spa
level: {
REPEATABLE READ
| READ COMMITTED
| READ UNCOMMITTED
| SERIALIZABLE
}
複製代碼
對於使用InnoDB存儲引擎的表來講,它的聚簇索引記錄中都包含必要的隱藏列:code
ReadView所解決的問題是使用READ COMMITTED和REPEATABLE READ隔離級別的事務中,不能讀到未提交的記錄,這須要判斷一下版本鏈中的哪一個版本是當前事務可見的。cdn
ReadView中主要包含4個比較重要的內容:htm
有了這些信息,這樣在訪問某條記錄時,只須要按照下邊的步驟判斷記錄的某個版本是否可見:blog
若是某個版本的數據對當前事務不可見的話,那就順着版本鏈找到下一個版本的數據,繼續按照上邊的步驟判斷可見性,依此類推,直到版本鏈中的最後一個版本。若是最後一個版本也不可見的話,那麼就意味着該條記錄對該事務徹底不可見,查詢結果就不包含該記錄。
在MySQL中,READ COMMITTED和REPEATABLE READ隔離級別的的一個很是大的區別就是它們生成ReadView的時機不一樣。
咱們這裏使用一個示例來解釋:
mysql> CREATE TABLE `t` (
`id` int(11) NOT NULL,
`k` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
insert into t(id, k) values(1,1) ;
複製代碼
事務A | 事務B |
---|---|
begin | |
begin | |
update t set k= k+1 where id=1; | |
commit; | |
update t set k = k+1 where id=1; | |
select k from t where id =1; | |
commit; |
在這個例子中,咱們作以下假設:
READ COMMITTED —— 每次讀取數據前都生成一個ReadView
繼續上面的例子,假設如今有一個使用READ COMMITTED隔離級別的事務開始執行:
# 使用READ COMMITTED隔離級別的事務
BEGIN;
# SELECT1:Transaction 100、200未提交
select k from t where id=1 ; # 獲得值爲1
複製代碼
這個SELECT1的執行過程以下:
以後,咱們把事務B的事務提交一下,而後再到剛纔使用READ COMMITTED隔離級別的事務中繼續查找,以下:
# 使用READ COMMITTED隔離級別的事務
BEGIN;
# SELECT1:Transaction 100、200均未提交
SELECT * FROM hero WHERE number = 1; # 獲得值爲1
# SELECT2:Transaction 200提交,Transaction 100未提交
SELECT * FROM hero WHERE number = 1; # 獲得值爲2
複製代碼
這個SELECT2的執行過程以下:
REPEATABLE READ —— 在第一次讀取數據時生成一個ReadView 假設如今有一個使用REPEATABLE READ隔離級別的事務開始執行:
# 使用REPEATABLE READ隔離級別的事務
BEGIN;
# SELECT1:Transaction 100、200未提交
SELECT * FROM hero WHERE number = 1; # 獲得值爲1
複製代碼
這個SELECT1的執行過程以下:
以後,咱們把事務B的事務提交一下 而後再到剛纔使用REPEATABLE READ隔離級別的事務中繼續查找:
# 使用REPEATABLE READ隔離級別的事務
BEGIN;
# SELECT1:Transaction 100、200均未提交
SELECT * FROM hero WHERE number = 1; # 獲得值爲1
# SELECT2:Transaction 200提交,Transaction 100未提交
SELECT * FROM hero WHERE number = 1; # 獲得值爲1
複製代碼
這個SELECT2的執行過程以下: