事物的并發問題:
事物的并發問題主要分四個方面,即丢失更新,髒讀,不可重複讀,幻讀。如果沒有鎖定且多個使用者同時通路一個資料庫,則當他們的事務同時使用相同的資料時,則可能會發生以上幾種問題。
1.丢失更新
當兩個或多個事務選擇同一行,然後基于最初標明的值更新該行時,會發生丢失更新問題。每個事務都不知道其它事務的存在。最後的更新将重寫由其它事務所作的更新,這樣就會導緻資料丢失。丢失更新可分為兩類:
第一類丢失更新
例如:
時 間 取款事務 轉賬事務
--------------------------------------------------------------------------------------------
T1 開始事務
T2 開始事務
T3 查詢賬戶餘額為1000元
T4 查詢賬戶餘額為1000元
T5 彙入100元把餘額改為1100元
T6 送出事務
T7 取出100元把餘額改為900元
T8 事務復原餘額恢複為1000元
由于每個事務都不知道其它事務的存在,若按以上時間次序并發執行,轉賬事務就會被取款事務的復原所覆寫。
第二類丢失更新
時 間 取款事務 轉賬事務
----------------------------------------------------------------------------------------------
T1 開始事務
T2 開始事務
T3 查詢賬戶餘額為1000元
T4 查詢賬戶餘額為1000元
T5 取出100元把餘額改為900元
T6 送出事務
T7
T8 彙入100元把餘額改為1100元
T9 送出事務
同理,若按以上時間次序執行,取款事務會被轉賬事務的更新所覆寫。
總結:丢失更新本質上是寫操作的沖突,解決辦法:事務A寫完并儲存後事務B再寫即可避免丢失更新問題。
2.髒讀(未确認的相關性)
如果一個事務讀取了另一個事務尚未送出的更新,則稱為髒讀。
時 間 取款事務 轉賬事務
T1 開始事務
T2 開始事務
T4
T6 查詢賬戶餘額為900元(髒讀)
T7 撤銷事務餘額恢複為1000元
T8 彙入100元把餘額改為1000元
T9 送出事務
由于轉賬事務讀取了取款事務尚未送出的更新,并且對資料做出了修改并送出,若取款事務因某種原因撤銷復原,例如逾時,則轉賬事務讀取的是假資料,會導緻使用者損失100元。
總結:髒讀的本質是讀與寫的沖突,解決辦法:一個事務寫完并确認無誤後另一個事務再進行讀取。
3.不可重複讀(不一緻的分析)
當事務多次通路同一行資料,并且每次讀取的資料不一緻的時候,即會發生不一緻的分析。
時 間 取款事務 轉賬事務
----------------------------------------------------------------------------------------------
T1 開始事務
T2 開始事務
T3 查詢賬戶餘額為1000元
T4 查詢賬戶餘額為1000元
T5 取出100元把餘額改為900元
T6 送出事務
T7 查詢賬戶餘額為900元
T8 餘額到底是1000元還是900元?
由于在轉賬事務第一次查詢到資料後,取款事務又對該行資料做出了修改并送出,而轉賬事務第二次讀取了修改後的資料,即得到了不同查詢結果。
總結:不可重複讀本質上是讀與寫的沖突,解決辦法:一個事務在另一個事務全部修改後再讀取資料。
4.幻讀
當一個事務的更新結果影響到另一個事務的時候,将會發生幻讀。事務第一次讀的行範圍顯示出其中一行已經不存在于第二次讀或後續讀中,因為該行已經被其它事務删除
時 間 注冊事務 統計事務
T1 開始事務
T2 開始事務
T3 統計注冊客戶總數為10000人
T4 注冊一個新使用者
T5 送出事務
T6 統計注冊客戶總數為10001人
T7 到底哪一個統計資料有效?
統計事務無法相信查詢的結果,因為查詢結果是不确定的,随時可能被其他事務改變。
對于實際應用,在一個事務中不會對相同的資料查詢兩次,假定統計事務在T3時刻統計注冊客戶的總數,執行SELECT語句,在T6時刻不再查詢資料庫,而 是直接列印統計結果為10000,這個統計結果與資料庫當中資料是有所出入的,确切的說,它反映的是T3時刻的資料狀态,而不是目前的資料狀态。應該根據 實際需要來決定是否允許虛讀。以上面的統計事務為例,如果僅僅想大緻了解一下注冊客戶總數,那麽可以允許虛讀;如果在一個事務中,會依據查詢結果來做出精 确的決策,那麽就必須采取必要的事務隔離措施,避免虛讀。
總結:幻讀的本質也是讀寫操作的沖突,解決辦法:讀完在寫。
事物的隔離級别:
解決并發問題的有效途徑即采取有效的隔離級别
資料庫系統采取了四種事務隔離級别供使用者選擇。(一般預設為)
1.SERIALIZABLE(串行化):一個事務在執行過程中完全看不到其他事務對資料庫所做的更新(事務執行的時候不允許别的事務并發執行。事務串行化執行,事務隻能一個接着一個地執行,而不能并發執行。)。
添加範圍鎖(比如表鎖,頁鎖等,關于range lock,我也沒有很深入的研究),直到transaction A結束。以此阻止其它transaction B對此範圍内的insert,update等操作。
幻讀,髒讀,不可重複讀等問題都不會發生。
2.Repeatable Read(可重複讀):一個事務在執行過程中可以看到其他事務已經送出的新插入的記錄,但是不能看到其他其他事務對已有記錄的更新。
對于讀出的記錄,添加共享鎖直到事務 A結束。其它事務 B對這個記錄的試圖修改會一直等待直到事務 A結束。
可能發生的問題:當執行一個範圍查詢時,可能會發生幻讀。
3.Read Commited(讀已送出資料):一個事務在執行過程中可以看到其他事務已經送出的新插入的記錄,而且能看到其他事務已經送出的對已有記錄的更新。
在事務 A中讀取資料時對記錄添加共享鎖,但讀取結束立即釋放。其它事務 B對這個記錄的試圖修改會一直等待直到A中的讀取過程結束,而不需要整個事務 A的結束。是以,在事務 A的不同階段對同一記錄的讀取結果可能是不同的。
可能發生的問題:不可重複讀
4.Read Uncommitted(讀未送出資料):一個事務在執行過程中可以看到其他事務沒有送出的新插入的記錄,而且能看到其他事務沒有送出的對已有記錄的更新。
不添加共享鎖。是以其它事務 B可以在事務 A對記錄的讀取過程中修改同一記錄,可能會導緻A讀取的資料是一個被破壞的或者說不完整不正确的資料。
另外,在事務A中可以讀取到事務B(未送出)中修改的資料。比如事務B對R記錄修改了,但未送出。此時,在事務A中讀取R記錄,讀出的是被B修改過的資料。
可能發生的問題:髒讀。
總結:對資料庫使用何種隔離級别要審慎分析,因為:
1. 維護一個最高的隔離級别雖然會防止資料的出錯,但是卻導緻了并行度的損失,以及導緻死鎖出現的可能性增加。
2. 然而,降低隔離級别,卻會引起一些難以發現的bug。
知識需刨根問底!