1. 程式人生 > >差點掉坑,MySQL一致性讀原來是有條件的

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

lock mysql block 影響 val creat read 原因 upd

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

以前一直以為在事務內讀到的數據不會受其他事務影響,後來發現只有普通的select語句才是一致性讀。如果是update, delete, select for update, select in share mode等語句是當前讀,讀的是數據庫最新數據, 下面是兩個例子。

加鎖讀

創建一個測試用的表, 然後插入一條測試用的數據


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,分別執行以下操作

時間點 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,分別執行以下操作,

時間點 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。

最後

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

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

MySQL實戰45講-8 事務到底是隔離的還是不隔離的

來源:https://segmentfault.com/a/1190000017539522

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