數據庫讀寫並發控制
在數據庫中,並發控制是指在多個用戶/進程/線程同時對數據庫進行操作時,如何保證事務的一致性和隔離性的,同時最大程度地並發。
當多個用戶/進程/線程同時對數據庫進行操作時,會出現3種沖突情形:- 讀-讀,不存在任何問題
- 讀-寫,有隔離性問題,可能遇到臟讀(會讀到未提交的數據) ,幻讀(重復讀)等。
- 寫-寫,可能丟失更新
要解決沖突,一種辦法是是鎖,即基於鎖的並發控制,比如2PL,這種方式開銷比較高,而且無法避免死鎖。
多版本並發控制(MVCC)是一種用來解決讀-寫沖突的無鎖並發控制,也就是為事務分配單向增長的時間戳,為每個修改保存一個版本,版本與事務時間戳關聯,讀操作只讀該事務開始前的數據庫的快照。 這樣在讀操作不用阻塞寫操作,寫操作不用阻塞讀操作的同時,避免了臟讀和不可重復讀
樂觀並發控制(OCC)是一種用來解決寫-寫沖突的無鎖並發控制,認為事務間爭用沒有那麽多,所以先進行修改,在提交事務前,檢查一下事務開始後,有沒有新提交改變,如果沒有就提交,如果有就放棄並重試。樂觀並發控制類似自選鎖。樂觀並發控制適用於低數據爭用,寫沖突比較少的環境。
悲觀鎖,正如其名,它指的是對數據被外界(包括本系統當前的其他事務,以及來自外部系統的事務處理)修改持保守態度,因此,在整個數據處理過程中,將數據處於鎖定狀態。 總結:悲觀鎖(Pessimistic Lock), 顧名思義,就是很悲觀,每次去拿數據的時候都認為別人會修改,所以每次在拿數據的時候都會上鎖,這樣別人想拿這個數據就會block直到它拿到鎖。傳統的關系型數據庫裏邊就用到了很多這種鎖機制,比如行鎖,表鎖等,讀鎖,寫鎖等,都是在做操作之前先上鎖。
樂觀鎖(Optimistic Lock), 顧名思義,就是很樂觀,每次去拿數據的時候都認為別人不會修改,所以不會上鎖,但是在更新的時候會判斷一下在此期間別人有沒有去更新這個數據,可以使用版本號等機制。樂觀鎖適用於多讀的應用類型,這樣可以提高吞吐量,像數據庫如果提供類似於write_condition機制的其實都是提供的樂觀鎖。
兩種鎖各有優缺點,不可認為一種好於另一種,像樂觀鎖適用於寫比較少的情況下,即沖突真的很少發生的時候,這樣可以省去了鎖的開銷,加大了系統的整個吞吐量。但如果經常產生沖突,上層應用會不斷的進行retry,這樣反倒是降低了性能,所以這種情況下用悲觀鎖就比較合適。
事務沒有隔離性會出現的問題:
臟讀:(同時操作都沒提交的讀取)
臟讀又稱無效數據讀出。一個事務讀取另外一個事務還沒有提交的數據叫臟讀。
例如:事務T1修改了一行數據,但是還沒有提交,這時候事務T2讀取了被事務T1修改後的數據,之後事務T1因為某種原因Rollback了,那麽事務T2讀取的數據就是臟的。
解決辦法:把數據庫的事務隔離級別調整到READ_COMMITTED
不可重復讀:(同時操作,事務一分別讀取事務二操作時和提交後的數據,讀取的記錄內容不一致)
不可重復讀是指在同一個事務內,兩個相同的查詢返回了不同的結果。
例如:事務T1讀取某一數據,事務T2讀取並修改了該數據,T1為了對讀取值進行檢驗而再次讀取該數據,便得到了不同的結果。 解決辦法:把數據庫的事務隔離級別調整到REPEATABLE_READ
幻讀:(和可重復讀類似,但是事務二的數據操作僅僅是插入和刪除,不是修改數據,讀取的記錄數量前後不一致)
例如:系統管理員A將數據庫中所有學生的成績從具體分數改為ABCDE等級,但是系統管理員B就在這個時候插入(註意時插入或者刪除,不是修改))了一條具體分數的記錄,當系統管理員A改結束後發現還有一條記錄沒有改過來,就好像發生了幻覺一樣。這就叫幻讀。
解決辦法:把數據庫的事務隔離級別調整到SERIALIZABLE_READ
數據庫讀寫並發控制