衆所周知,在設定了隔離等級爲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
不受其餘事務影響,因此讀到的數據都是同一版本,而在加鎖讀的狀況下采起的是讀最新的數據,因此讀到的數據是最新提交的數據。事務
在進行數據變動操做的時候,也會拿到最新的數據,用的仍是上面的表,插入一條測試數據內存
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 事務究竟是隔離的仍是不隔離的