Mysql加锁过程详解(2)-关于mysql 幻读理解
這里給出 mysql 幻讀的比較形象的場景:
?
users: id 主鍵
1、T1:select * from users where id = 1;
2、T2:insert into `users`(`id`, `name`) values (1, 'big cat');
3、T1:insert into `users`(`id`, `name`) values (1, 'big cat');
?
T1 :主事務(wù),檢測表中是否有 id 為 1 的記錄,沒有則插入,這是我們期望的正常業(yè)務(wù)邏輯。
T2 :干擾事務(wù),目的在于擾亂 T1 的正常的事務(wù)執(zhí)行。
?
在 RR 隔離級別下,1、2 是會正常執(zhí)行的,3 則會報(bào)錯(cuò)主鍵沖突,對于 T1 的業(yè)務(wù)來說是執(zhí)行失敗的,這里 T1 就是發(fā)生了幻讀,因?yàn)門1讀取的數(shù)據(jù)狀態(tài)并不能支持他的下一步的業(yè)務(wù),見鬼了一樣。
在 Serializable 隔離級別下,1 執(zhí)行時(shí)是會隱式的添加 gap 共享鎖的,從而 2 會被阻塞,3 會正常執(zhí)行,對于 T1 來說業(yè)務(wù)是正確的,成功的扼殺了擾亂業(yè)務(wù)的T2,對于T1來說他讀取的狀態(tài)是可以拿來支持業(yè)務(wù)的。
?
所以 mysql 的幻讀并非什么讀取兩次返回結(jié)果集不同,而是事務(wù)在插入事先檢測不存在的記錄時(shí),驚奇的發(fā)現(xiàn)這些數(shù)據(jù)已經(jīng)存在了,之前的檢測讀獲取到的數(shù)據(jù)如同鬼影一般。
這里要靈活的理解讀取的意思,第一次select是讀取,第二次的 insert 其實(shí)也屬于隱式的讀取,只不過是在 mysql 的機(jī)制中讀取的,插入數(shù)據(jù)也是要先讀取一下有沒有主鍵沖突才能決定是否執(zhí)行插入。
不可重復(fù)讀側(cè)重表達(dá) 讀-讀,幻讀則是說 讀-寫,用寫來證實(shí)讀的是鬼影。
?
下面例子版本
SELECT VERSION();
?
?
?
例子1,讀提交
| ?????????????????????????????????????????????????????? a | b |
| SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; | |
| SET AUTOCOMMIT=0; | |
| 1.不可重復(fù)讀 | |
| begin | begin |
| INSERT test VALUES(1,1); | ? |
| SELECT * FROM test; ? ? | SELECT * FROM test; ? ? ? |
| commit | ? |
| ? | SELECT * FROM test; |
| ? | ? ? |
| ? | COMMIT |
| B在一個(gè)事務(wù)的查詢的結(jié)果變了,不可重復(fù)讀 | |
| 2.鎖 | |
| begin | begin |
| INSERT test VALUES(2,2); | ? |
| ? | SELECT * FROM test; ? ? |
| ? | INSERT test VALUES(2,2); |
| ? | Lock wait timeout exceeded; try restarting transaction |
| COMMIT | COMMIT |
| ? | |
| ? | begin |
| ? | INSERT test VALUES(3,3); |
| ? | INSERT test VALUES(4,4); |
| ? | COMMIT |
| ? | ? |
| BEGIN | BEGIN |
| SELECT COUNT(*) FROM test WHERE a>2; ? | SELECT COUNT(*) FROM test WHERE a>2; ? |
| ? | ? |
| INSERT test VALUES(5,5); | ? |
| SELECT COUNT(*) FROM test WHERE a>2; ? | SELECT COUNT(*) FROM test WHERE a>2; ? |
| ? | ? |
| COMMIT | ? |
| SELECT COUNT(*) FROM test WHERE a>2; | SELECT COUNT(*) FROM test WHERE a>2; |
| ? ? | ? ? |
| ? | |
?
?
例子2:重復(fù)讀
?
| ?????????????????????????????????????????????????????? a | b |
| SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ; | |
| SET AUTOCOMMIT=0; | |
| 1.可重復(fù)讀 | |
| begin | begin |
| INSERT test VALUES(1,1); | ? |
| SELECT * FROM test; ? ? | SELECT * FROM test; ? ? ? |
| commit | ? |
| ? | SELECT * FROM test; |
| ? | ? ? |
| ? | COMMIT |
| ? | BEGIN |
| ? | SELECT * FROM test; |
| ? | ? ? |
| ? | COMMIT |
| B在一個(gè)事務(wù)的查詢的沒變 | |
| 2鎖 | |
| begin | begin |
| INSERT test VALUES(2,2); | ? |
| ? | SELECT * FROM test; ? ? |
| ? | INSERT test VALUES(2,2); |
| ? | Lock wait timeout exceeded; try restarting transaction |
| COMMIT | COMMIT |
| 3(幻讀) | |
| BEGIN | BEGIN |
| INSERT test VALUES(3,3); | ? |
| SELECT * FROM test; ? ? | SELECT * FROM test; ? ? |
| COMMIT | ? |
| ? | SELECT * FROM test; ? ? |
| ? | INSERT test VALUES(3,3); |
| ? | Duplicate entry '3' for key 'PRIMARY' |
| ? | COMMIT |
| ? | BEGIN |
| ? | SELECT * FROM test; ? ? |
| ? | COMMIT |
| 幻讀,b明明查到?jīng)]有,插入時(shí)候提示主鍵沖突,剛剛查詢沒有,出現(xiàn)幻覺? | |
| ? | |
| ? | ? |
| ? | begin |
| ? | INSERT test VALUES(4,4); |
| ? | COMMIT |
| 4.可重復(fù)讀 | |
| BEGIN | BEGIN |
| SELECT COUNT(*) FROM test WHERE a>2; ? | SELECT COUNT(*) FROM test WHERE a>2; ? |
| ? | ? |
| INSERT test VALUES(5,5); | ? |
| SELECT COUNT(*) FROM test WHERE a>2; ? | SELECT COUNT(*) FROM test WHERE a>2; ? |
| ? | ? |
| COMMIT | ? |
| BEGIN | ? |
| SELECT COUNT(*) FROM test WHERE a>2; | SELECT COUNT(*) FROM test WHERE a>2; |
| ? ? | ? ? |
| COMMIT | COMMIT |
| ? | |
?
?
?
?
?
?
網(wǎng)上很多說范圍啊,count等等都是不對的,不用于幻讀
?
總結(jié)
以上是生活随笔為你收集整理的Mysql加锁过程详解(2)-关于mysql 幻读理解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 结队-五子棋游戏-项目进度
- 下一篇: 浅析Java线程池 ExecutorSe