當多個線程都開啓事務操做數據庫中的數據時,數據庫系統要能進行隔離操做,以保證各個線程獲取數據的準確性, 因此, 對於不一樣的事務,採用不一樣的隔離級別 會 有不一樣的結果。數據庫
若是不考慮事務的隔離性, 那麼 會發生 下表所示的 3 種問題:併發
不可重複讀 是因爲 事務併發修改 同一條 記錄 致使的 ,要避免這種狀況,最簡單的方法就是對要修改的記錄加鎖,這 會 致使鎖競爭加重 ,影響性能。另外一種方法是經過 MVCC 能夠在無鎖的狀況下,避免不可重複讀。oracle
幻讀是因爲併發事務增長記錄致使的,這個不能像不可重複讀經過記錄加鎖解決,由於對於新增的記錄根本沒法加鎖。須要將事務串行化,才能避免幻讀。ide
在 SQL 標準中定義了 4 種隔離級別,每一種級別都規定了一個事務中所作的修改,哪些是在事務內和事務間可見的,哪些是不可見的。較低級別的隔離一般能夠執行更高的併發,系統的開銷也更低。 SQL 標準定義的四個隔離級別爲: Read Uncommitted ( 未提交讀 ) 、 Read Committed (提交讀)、 Repeatable Read (可重複讀)、 Serializable (可串行化) ,下面分別介紹。性能
不一樣的隔離級別有不一樣的現象,並有不一樣的鎖 和 併發機制,隔離級別越高,數據庫的併發性 能 就越差, 4 種事 隔離級別與併發性能的關係:線程
若是沒有事務隔離,會出現什麼樣的狀況?3d
假設咱們如今有這樣一張表(T),裏面記錄了不少牛人的名字,咱們不進行事務的隔離看看會發生什麼呢?blog
第一天,事務A訪問了數據庫,它幹了一件事情,往數據庫里加上了新來的牛人的名字,可是沒有提交事務。索引
insert into T values (4, '牛D');
這時,來了另外一個事務B,他要查詢全部牛人的名字。事務
select Name from T;
這時,若是沒有事務之間沒有有效隔離,那麼事務B返回的結果中就會出現「牛D」的名字。這就是「髒讀(dirty read)」。
次日,事務A訪問了數據庫,他要查看ID是1的牛人的名字,因而執行了
select Name from T where ID = 1;
這時,事務B來了,由於ID是1的牛人更名字了,因此要更新一下,而後提交了事務。
update T set Name = '不牛' where ID = 1;
接着,事務A還想再看看ID是1的牛人的名字,因而又執行了
select Name from T where ID = 1;
結果,兩次讀出來的ID是1的牛人名字居然不相同,這就是不可重複讀(unrepeatable read)。
第三天,事務A訪問了數據庫,他想要看看數據庫的牛人都有哪些,因而執行了
select * from T;
這時候,事務B來了,往數據庫加入了一個新的牛人。
insert into T values(4, '牛D');
這時候,事務A忘了剛纔的牛人都有哪些了,因而又執行了。
select * from T;
結果,第一次有三個牛人,第二次有四個牛人。
相信這個時候事務A就蒙了,剛纔發生了什麼?這種狀況就叫「幻讀(phantom problem)」。
爲了防止出現髒讀、不可重複讀、幻讀等狀況,咱們就須要根據咱們的實際需求來設置數據庫的隔離級別。下面介紹下這方面內容。
數據庫事務隔離級別
數據庫事務隔離級別分爲四種(級別遞減):
一、Serializable (串行化):最嚴格的級別,事務串行執行,資源消耗最大;
二、REPEATABLE READ(重複讀) :保證了一個事務不會修改已經由另外一個事務讀取但未提交(回滾)的數據。避免了「髒讀取」和「不可重複讀取」的狀況,但不能避免「幻讀」,可是帶來了更多的性能損失。
三、READ COMMITTED (提交讀):大多數主流數據庫的默認事務等級,保證了一個事務不會讀到另外一個並行事務已修改但未提交的數據,避免了「髒讀取」,但不能避免「幻讀」和「不可重複讀取」。該級別適用於大多數系統。
四、Read Uncommitted(未提交讀) :事務中的修改,即便沒有提交,其餘事務也能夠看獲得,會致使「髒讀」、「幻讀」和「不可重複讀取」。
髒讀、不可重複讀、幻讀:
一個數據庫可能擁有多個訪問客戶端,這些客戶端併發訪問數據庫時,若沒有采起必要的隔離措施,存在如下問題,這些問題分爲5類,包括3類數據讀問題:髒讀、不可重複讀和幻讀。兩類數據更新問題:第一類丟失更新、第二類丟失更新。
一、髒讀
A事務讀取B事務還沒有提交的更改數據,並在這個數據的基礎上進行操做,這時候若是事務B回滾,那麼A事務讀到的數據是不被認可的。例如常見的取款事務和轉帳事務:
二、不可重複讀
不可重複讀是指A事務讀取了B事務已經提交的更改數據。假如A在取款事務的過程當中,B往該帳戶轉帳100,A兩次讀取的餘額發生不一致。
三、幻讀
A事務讀取B事務提交的新增數據,會引起幻讀問題。幻讀通常發生在計算統計數據的事務中,例如銀行系統在同一個事務中兩次統計存款帳戶的總金額,在兩次統計中,恰好新增了一個存款帳戶,存入了100,這時候兩次統計的總金額不一致。
注意:不可重複讀和幻讀的區別是:前者是指讀到了已經提交的事務的更改數據(修改或刪除),後者是指讀到了其餘已經提交事務的新增數據。對於這兩種問題解決採用不一樣的辦法,防止讀到更改數據,只需對操做的數據添加行級鎖,防止操做中的數據發生變化;二防止讀到新增數據,每每須要添加表級鎖,將整張表鎖定,防止新增數據(oracle採用多版本數據的方式實現)。
四、通俗解釋:
髒讀:所謂的髒讀,其實就是讀到了別的事務回滾前的髒數據。好比事務B執行過程當中修改了數據X,在未提交前,事務A讀取了X,而事務B卻回滾了,這樣事務A就造成了髒讀。
也就是說,當前事務讀到的數據是別的事務想要修改爲爲的可是沒有修改爲功的數據。
不可重複讀:事務A首先讀取了一條數據,而後執行邏輯的時候,事務B將這條數據改變了,而後事務A再次讀取的時候,發現數據不匹配了,就是所謂的不可重複讀了。
也就是說,當前事務先進行了一次數據讀取,而後再次讀取到的數據是別的事務修改爲功的數據,致使兩次讀取到的數據不匹配,也就照應了不可重複讀的語義。
幻讀:事務A首先根據條件索引獲得N條數據,而後事務B改變了這N條數據以外的M條或者增添了M條符合事務A搜索條件的數據,致使事務A再次搜索發現有N+M條數據了,就產生了幻讀。
也就是說,當前事務讀第一次取到的數據比後來讀取到數據條目少。
不可重複讀和幻讀比較:
二者有些類似,可是前者針對的是update或delete,後者針對的insert。
爲何會出現「髒讀」?由於沒有「select」操做沒有規矩。
爲何會出現「不可重複讀」?由於「update」操做沒有規矩。
爲何會出現「幻讀」?由於「insert」和「delete」操做沒有規矩。
「讀未提(Read Uncommitted)」能預防啥?啥都預防不了。
「讀提交(Read Committed)」能預防啥?使用「快照讀(Snapshot Read)」,避免「髒讀」,可是可能出現「不可重複讀」和「幻讀」。
「可重複讀(Repeated Red)」能預防啥?使用「快照讀(Snapshot Read)」,鎖住被讀取記錄,避免出現「髒讀」、「不可重複讀」,可是可能出現「幻讀」。
「串行化(Serializable)」能預防啥?排排坐,吃果果,有效避免「髒讀」、「不可重複讀」、「幻讀」,不過效果誰用誰知道。