一、緣由
眾所周知MySQL從5.5.8開始,Innodb就是默認的存儲引擎,Innodb最大的特點是:支持事務、支持行級鎖。
既然支持事務,那么就會有處理並發事務帶來的問題:更新丟失、臟讀、不可重復讀、幻讀;相應的為了解決這四個問題,
就產生了事務隔離級別:未提交讀(Read uncommitted),已提交讀(Read committed),可重復讀(Repeatable read),可序列化(Serializable)。
之前在看事務隔離級別的時候,基本是一知半解。再次碰到,還是漿糊,所以這里打算徹底搞明白、清楚,也好后續的學習。
二、解決辦法:
1、概念說明:
更新丟失:最后的更新覆蓋了其他事務之前的更新,而事務之間並不知道,發生更新丟失。更新丟失,可以完全避免,應用對訪問的數據加鎖即可。
臟讀:(針對未提交的數據)一個事務在更新一條記錄,未提交前,第二個事務讀到了第一個事務更新后的記錄,那么第二個事務就讀到了臟數據,會產生對第一個未提交
數據的依賴。一旦第一個事務回滾,那么第二個事務讀到的數據,將是錯誤的臟數據。
不可重復讀:(讀取數據本身的對比)一個事務在讀取某些數據后的一段時間后,再次讀取這個數據,發現其讀取出來的數據內容已經發生了改變,就是不可重復讀。
幻讀:(讀取結果集條數的對比)一個事務按相同的查詢條件查詢之前檢索過的數據,確發現檢索出來的結果集條數變多或者減少(由其他事務插入、刪除的),類似產生幻覺。
2、不可重復讀和幻讀的區別:
不可重復讀
重點是修改:同樣的條件下,你讀取過的數據,再次讀取發現值不一樣了。
例子:(只是為了說明區別,沒有事務隔離級別約束)
事務1:Jack讀取自己的工資為1000,事務並沒有結束:
select salary from employee empId ="Jack";
事務2:財務人員修改了Jack的工資為2000,並提交事務(事務1不知道):
update employee set salary = 2000;
commit;
在事務1中,再次讀取Jack的工資時,工資變為2000
select salary from employee empId ="Jack";
在一個事務中前后兩次讀取的結果值並不一致,導致了不可重讀讀
幻讀
重點在於新增或者刪除(數據條數的變化):同樣條件下,第一次和第二次讀出來的記錄條數不一樣。
例子:
目前工資為1000的員工有10人。
事務1:讀取所有工資為1000的員工,事務進行中
select count(*) from employee where salary = 1000;
共讀取到10條記錄。
事務2:同時插入一條新的員工記錄,工資也為1000
insert into employee(empId, salary) values('Tom', 1000);
commit;
事務1:再次讀取所有工資為1000的員工
select count(*) from employee where salary = 1000;
共讀取到了11條記錄,這樣就像產生了幻讀。
三、事務隔離級別:
並發處理帶來的問題中,更新丟失可以完全避免,由應用對數據加鎖即可。臟讀、不可重讀度、幻讀,其實都是數據庫的一致性問題,必須由一定的事務隔離機制來解決。
其中一種方法是:不用加鎖,通過一定的機制生成一個數據請求時間點的一致性快照,並用這個快照來提供一個界別的一致性讀取。從用戶的角度看,好像是數據庫提偶拱了
統一數據的多個版本。這種技術叫做:數據庫多版本並發控制,MVCC 多版本數據庫。
事務隔離的本質是使事務在一定程度上串行化執行,顯然和並發機制是矛盾的。數據庫的事務隔離越嚴格,並發負作用越小,代價越高(影響並發訪問了唄)。
為了解決隔離和並大的矛盾,IOS SQL92規定了4個隔離級別。(隔離==串行)

MySQL 默認的級別是:Repeatable read 可重復讀。
