在MySQL的眾多存儲引擎中,只有InnoDB支持事務(wù),所有這里說的事務(wù)隔離級別指的是InnoDB下的事務(wù)隔離級別。
MySQL 是支持多事務(wù)并發(fā)執(zhí)行的。否則來一個事務(wù)處理一個請求,處理一個人請求的時候,其它事務(wù)都等著,那估計都沒人敢用MySQL作為數(shù)據(jù)庫,因為用戶體驗太差,估計都要砸鍵盤了。
既然事務(wù)可以并發(fā)操作,這里就有一些問題:一個事務(wù)在寫數(shù)據(jù)的時候,另一個事務(wù)要讀這行數(shù)據(jù),該怎么處理?一個事務(wù)在寫數(shù)據(jù),另一個數(shù)據(jù)也要寫這行數(shù)據(jù),又該怎么處理這個沖突?
這就是并發(fā)事務(wù)所產(chǎn)生的一些問題。具體來說就是:臟讀
、不可重復(fù)讀
和幻讀
。
概念說明
以下幾個概念是事務(wù)隔離級別要實際解決的問題,所以需要搞清楚都是什么意思。
臟讀
臟讀指的是讀到了其他事務(wù)未提交的數(shù)據(jù),未提交意味著這些數(shù)據(jù)可能會回滾,也就是可能最終不會存到數(shù)據(jù)庫中,也就是不存在的數(shù)據(jù)。讀到了并一定最終存在的數(shù)據(jù),這就是臟讀。
臟讀最大的問題就是可能會讀到不存在的數(shù)據(jù)。比如在上圖中,事務(wù)B的更新數(shù)據(jù)被事務(wù)A讀取,但是事務(wù)B回滾了,更新數(shù)據(jù)全部還原,也就是說事務(wù)A剛剛讀到的數(shù)據(jù)并沒有存在于數(shù)據(jù)庫中。
從宏觀來看,就是事務(wù)A讀出了一條不存在的數(shù)據(jù),這個問題是很嚴(yán)重的。
不可重復(fù)讀
不可重復(fù)讀指的是在一個事務(wù)內(nèi),最開始讀到的數(shù)據(jù)和事務(wù)結(jié)束前的任意時刻讀到的同一批數(shù)據(jù)出現(xiàn)不一致的情況。
事務(wù) A 多次讀取同一數(shù)據(jù),但事務(wù) B 在事務(wù)A多次讀取的過程中,對數(shù)據(jù)作了更新并提交,導(dǎo)致事務(wù)A多次讀取同一數(shù)據(jù)時,結(jié)果 不一致。
幻讀
臟讀、不可重復(fù)讀上面的圖文都很好的理解,對于幻讀網(wǎng)上有很多文章都是這么解釋的
幻讀錯誤的理解:說幻讀是 事務(wù)A 執(zhí)行兩次 select 操作得到不同的數(shù)據(jù)集,即 select 1 得到 10 條記錄,select 2 得到 15 條記錄。這其實并不是幻讀,既然第一次和第二次讀取的不一致,那不還是不可重復(fù)讀嗎,所以這是不可重復(fù)讀的一種。
正確的理解應(yīng)該是
幻讀,并不是說兩次讀取獲取的結(jié)果集不同,幻讀側(cè)重的方面是某一次的 select 操作得到的結(jié)果所表征的數(shù)據(jù)狀態(tài)無法支撐后續(xù)的業(yè)務(wù)操作。更為具體一些:select 某記錄是否存在,不存在,準(zhǔn)備插入此記錄,但執(zhí)行 insert 時發(fā)現(xiàn)此記錄已存在,無法插入,此時就發(fā)生了幻讀。
舉例
假設(shè)有張用戶表,這張表的 id 是主鍵。表中一開始有4條數(shù)據(jù)。
我們再來看下出現(xiàn) 幻讀 的場景
這里是在RR級別下研究(可重復(fù)讀),因為 RU / RC 下還會存在臟讀、不可重復(fù)讀,故我們就以 RR 級別來研究 幻讀,排除其他干擾。
1、事務(wù)A,查詢是否存在 id=5 的記錄,沒有則插入,這是我們期望的正常業(yè)務(wù)邏輯。
2、這個時候 事務(wù)B 新增的一條 id=5 的記錄,并提交事務(wù)。
3、事務(wù)A,再去查詢 id=5 的時候,發(fā)現(xiàn)還是沒有記錄(因為這里是在RR級別下研究(可重復(fù)讀),所以讀到依然沒有數(shù)據(jù))
4、事務(wù)A,插入一條 id=5 的數(shù)據(jù)。
最終 事務(wù)A 提交事務(wù),發(fā)現(xiàn)報錯了。這就很奇怪,查的時候明明沒有這條記錄,但插入的時候 卻告訴我 主鍵沖突,這就好像幻覺一樣。這才是所有的幻讀。
不可重復(fù)讀側(cè)重表達(dá) 讀-讀,幻讀則是說 讀-寫,用寫來證實讀的是鬼影。
事務(wù)的隔離級別
上述所說的"臟讀",“不可重復(fù)讀”,"幻讀"這些問題,其實就是數(shù)據(jù)庫讀一致性問題,必須由數(shù)據(jù)庫提供的事務(wù)隔離機(jī)制來進(jìn)行解決。
首先說讀未提交,它是性能最好,也可以說它是最野蠻的方式,因為它壓根兒就不加鎖,所以根本談不上什么隔離效果,可以理解為沒有隔離。
再來說串行化。串行化就相當(dāng)于上面所說的,處理一個人請求的時候,別的人都等著。讀的時候加共享鎖,也就是其他事務(wù)可以并發(fā)讀,但是不能寫。寫的時候加排它鎖,其他事務(wù)不能并發(fā)寫也不能并發(fā)讀。
最后說讀提交和可重復(fù)讀。這兩種隔離級別是比較復(fù)雜的,既要允許一定的并發(fā),又想要兼顧的解決問題。MySQL默認(rèn)事務(wù)隔離級別為可重復(fù)讀(RR),oracle默認(rèn)事務(wù)隔離級別為讀已提交(RC),
數(shù)據(jù)庫的事務(wù)隔離越嚴(yán)格,并發(fā)副作用越小,但付出的代價越大;因為事務(wù)隔離本質(zhì)就是使事務(wù)在一定程度上處于串行狀態(tài),這本身就是和并發(fā)相矛盾的。
同時,不同的應(yīng)用對讀一致性和事務(wù)隔離級別是不一樣的,比如許多應(yīng)用對數(shù)據(jù)的一致性沒那么個高要求,相反,對并發(fā)有一定要求。
審核編輯:劉清
-
MySQL
+關(guān)注
關(guān)注
1文章
802瀏覽量
26444
發(fā)布評論請先 登錄
相關(guān)推薦
評論