前面我們知道了 db 為了增加 throughput,所以在資料庫系統之中會同時有許許多多的 tx 同時運行,
假設 db 完全沒有任何上鎖完全拋開束縛的情況下,分別會帶來什麼問題,
然後慢慢的為了解決問題而加上上一篇我們介紹 lock,
再來更近一步的介紹事務隔離(Isolation)
的概念。
居然可以讀到尚未 commit 的值!
tx2 做 commit 可能還好,
當 tx2 做 rollback 後,事情就像沒發生過一樣,
但 tx1 還是一臉矇逼的看著不知道發生什麼事情。
表示不論在做 select 或 update 的過程中,都直接改進 db。
當 rollback 就像沒發生一樣死無對證,但其他 tx 卻已經讀到了修改個過程。
能夠讀取沒有被 commit 的值,我們稱為發生Dirty read
,
因此我們要規定 commit之後的值才可以被其他tx讀取
只可以讀取commit完的資料
OK,這下只可以讀取 commit 完的資料,沒 commit 的不給讀,
因此我們將 tx 的 begin 一直到 commit/rollback 的過程中,都不能被其他 tx 給讀取
再拿上一個的範例來看,tx1 順利的只抓到 time1 相同的值,而 tx2 就算做 update 也不會影響到 tx1 在 time5 的 select
雖然已經告訴大家只可以讀已經 commit 的資料,但是!
確實是只讀 commit 的資料,但怎麼會同一個 tx 讀出的結果居然不同。
這樣實在太奇怪了,那麼這樣哪個才是對的呢?
雖然只可以讀已經 commit 的資料,但是 tx1 會因時間差而有不同結果,明顯地受到其他 tx 的影響。
因在同一個 tx 讀取第二次會有不同結果,感覺怪怪的
我們稱為發生 non repeatable read
,不能被重複讀取,否則會怪怪的
因此我們要規定 來! 我讀過後不能改!
加上 share lock!
只可以讀取commit完的資料,且讀過的上鎖不給改
OK,這下只可以讀取 commit 完的資料,沒 commit 的不給讀,
而且我讀過後不能改!果真順利的得到相同的結果,因為沒有其他 tx 可以改了。
雖然已經告訴大家只可以讀過後不能改!,但是!
我插入!
鎖並沒有鎖在新的紀錄上啊!
我調皮,你咬我啊!
確實是讀過的不能改,但怎麼會同一個 tx 統計出來的不同,這下兩次的 count 就不一樣了 QQ
插入時,鎖並沒有鎖在新的紀錄上,因此發生了漏網之魚,
我們稱為發生Phantom Read幻讀
,來! 我就鎖整張表!加上意向鎖(Intention Lock)
只可以讀取commit完的資料,且讀過的上鎖不給改,且查詢時鎖整張表
查詢時直接將整張表上鎖,連插入的機會都沒有,
果然 insert 時被 blocking 住了。
如果要解決上面的問題,必須在資料或表加上鎖。
dirty read: 問題為 read uncommit。範圍為一列。
non repeatable read: 問題為 read after update。範圍為一列。
Phantom read: 問題為 read after insert or delete。範圍為一區間。
既然我們知道了問題,也知道了在什麼時候加上鎖,
因此,我們提出了一個叫隔離層級
的概念,讓每個 tx 執行的過程中,
就像每個 tx 依序執行,不知道有其他 tx 一樣,如同隔離
一般。
至於如何如何隔離,相互的影響程度為何,
我們就可以利用個別的問題與 lock 建立出對應的搭配,分別有四個隔離層級:
Read什麼鎖都沒加上
,可以讀到尚未 commit 的值。只可讀 commit 之後的值
。讀過的上鎖不給改
。查詢時鎖整張表
。我們看了上面的解釋自然而然的可以整理出這張表。
最後,我們對應問題與隔離層級整理出這張表。
表格不是用背的,相信了解了上面解決問題的過程,即可將該表格整理出來。
在解決問題的過程中,
Read commit: 只讀有 commit 過的,解決了 dirty read。
Repeatable: 讀過的不能改,解決了 Non-Repeatable。
Serializable: 使用過的地方會標上標記,解決了 Phantom read。
得知了 isolation level 並不是避免 race condition 的出現,
而是對於 race condition side effect 的最大容忍程度
。
讓 Race condition 發生後不出現「致命錯誤」,讓系統向不致命的方向發生錯誤。