差點掉坑,MySQL一致性讀原來是有條件的

衆所周知,在設定了隔離等級爲Repeatable Read及以上時,InnoDB 能夠實現數據的一致性讀。換句話來講,就是事務執行的任意時刻,讀取到的數據是同一個快照,不會受到其餘事務的更新影響。mysql

之前一直覺得在事務內讀到的數據不會受其餘事務影響,後來發現只有普通的select語句纔是一致性讀。若是是update, delete, select for update, select in share mode等語句是當前讀,讀的是數據庫最新數據, 下面是兩個例子。sql

加鎖讀

建立一個測試用的表, 而後插入一條測試用的數據數據庫

create table test_innodb_read(
    id int not null primary key,
    value int
) engine = InnoDB charset=utf8;
insert into test_innodb_read values (1, 1);

當前autocommit和隔離等級以下測試

db83-3306>>select @@autocommit;
+--------------+
| @@autocommit |
+--------------+
|            1 |
+--------------+
1 row in set (0.00 sec)

db83-3306>>select @@transaction_isolation;
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| REPEATABLE-READ         |
+-------------------------+
1 row in set (0.00 sec)

而後開啓兩個 Session,分別執行如下操做code

時間點 SessionA 結果 SessionB
1 begin
select * from test_innodb_read where id=1
1
2 update test_innodb_read set value = 2 where id=1
3 select * from test_innodb_read where id=1 1
4 select * from test_innodb_read where id=1 lock in share mode 2

因爲設置了自動提交,因此SessionB的更新語句執行完就已經提交了,從結果能夠看到普通的Select不受其餘事務影響,因此讀到的數據都是同一版本,而在加鎖讀的狀況下采起的是讀最新的數據,因此讀到的數據是最新提交的數據。事務

DML 操做

在進行數據變動操做的時候,也會拿到最新的數據,用的仍是上面的表,插入一條測試數據內存

insert into test_innodb_read values (2, 1);

而後開啓兩個 Session,分別執行如下操做,get

時間點 SessionA 結果 SessionB
1 begin
select * from test_innodb_read where id=2
1
2 update test_innodb_read set value = 2 where id=2
3 select * from test_innodb_read where id=2 1
4 update test_innodb_read set value=value+1 where id=2
5 select * from test_innodb_read where id=2 3

SessionA在時間點 5 查看數據拿到的是 3 而不是 2,緣由是,事務在對數據進行更新操做時(時間點4),會先讀取一次數據,此次讀取的不是事務開始版本,而是數據的最新提交的值 2。若是不讀取最新數據的話,就等於覆蓋了SessionB的更新,因此讀到的是 2,最後獲得的數據是 3。it

最後

當我知道這個知識點後,感受背後一涼,之前寫代碼的時候,喜歡在事務裏先把數據查出來,內存中相加減,再存庫,如今想一想這樣作就是BUG啊,坑...io

不得不說,極客時間上面的這個MySQL課程仍是很值的,至少讓我推翻了之前的想法

MySQL實戰45講-8 事務究竟是隔離的仍是不隔離的
相關文章
相關標籤/搜索