數據庫的並發操作
事務
事務(Transaction)是用戶定義的一個數據庫操作序列,這些操作要么全做,要么全不做,是一個不可分割的工作單位。
事務是恢復和並發控制的基本單位
事務的ACID特性:
-
原子性(Atomicity):事務是數據庫的邏輯工作單位
-
一致性(Consistency):事務執行的結果必須是使數據庫從一個一致性狀態變
到另一個一致性狀態
-
隔離性(Isolation):一個事務的執行不能被其他事務干擾
-
持續性(Durability ):一個事務一旦提交,它對數據庫中數據的改變就應該
是永久性的。
並發控制
事務是並發控制的基本單位
並發控制機制的任務
- 對並發操作進行正確調度
- 保證事務的隔離性
- 保證數據庫的一致性
並發操作帶來的數據不一致性
- 丟失修改(Lost Update)
- 不可重復讀(Non-repeatable Read)
- 幻讀(Phantom Read)
- 讀“臟”數據(Dirty Read)
丟失修改:兩個事務T-1和T-2讀入同一數據並修改,T-2的提交結果破壞了T-1提交 的結果,導致T-1的修改被丟失。(修改-修改沖突)
不可重復讀:事務1讀取某一數據,事務2對其做了修改;當事務1再次讀該數據 時,得到與前一次不同的值(讀-更新沖突)
幻讀:事務T-1按一定條件從數據庫中讀取了某些數據記錄,事務T-2刪除(插入) 了其中部分記錄 ,當T-1再次按相同條件讀取數據時,發現某些記錄神秘地 消失(出現)了。(讀-插入/刪除沖突)
臟數據: 事務T-1修改某一數據,並將其寫回磁盤;事務T-2讀取同一數據后,T-1由 於某種原因被撤銷這時T-1,已修改過的數據恢復原值,T-2讀到的數據就 與數據庫中的數據不一致T-2讀到的數據就為“臟”數據,即不正確的數據(修 改-讀沖突)
數據不一致性:由於並發操作破壞了事務的隔離性
並發控制就的目的就是要用正確的方式調度並發操作,使一個用戶事務的執行不受其他事務的干擾,從而避免造成數據的不一致性
並發控制的主要技術
- 封鎖(Locking)
- 時間戳
- 樂觀控制法
- 多版本並發控制
封鎖
封鎖:封鎖就是事務T在對某個數據對象(例如表、記錄等)操作之前,先向系統發 出請求,對其加鎖
封鎖是實現並發控制的一個非常重要的技術
基本封鎖類型
- 排它鎖(Exclusive Locks,簡記為X鎖)又叫寫鎖
- 共享鎖(Share Locks,簡記為S鎖)又叫讀鎖
排它鎖:若事務T對數據對象A加上X鎖,則只允許T讀取和修改A,其它任何事務都 不能再對A加任何類型的鎖,直到T釋放A上的鎖保證其他事務在T釋放A上 的鎖之前不能再讀取和修改A
共享鎖:若事務T對數據對象A加上S鎖,則事務T可以讀A,但不能修改A,其它事務 只能再對A加S鎖,而不能加X鎖,直到T釋放A上的S鎖,保證其他事務可 以讀A,但在T釋放A上的S鎖之前,不能對A做任何修改
封鎖協議
封鎖協議:在運用X鎖和S鎖對數據對象加鎖時,需要約定一些規則,這些規則為 Locking Protocol
對封鎖方式規定不同的規則,就形成了各種不同的封鎖協議,在不同的程度上保證並發操作的正確調度。
封鎖協議決定了事物的隔離級別。
三級封鎖協議
- 一級封鎖協議
- 二級封鎖協議
- 三級封鎖協議
一級封鎖協議:事務T在修改數據R之前必須先對其加X鎖,直到事務結束才釋放。
正常結束(COMMIT)
非正常結束(ROLLBACK)
一級封鎖協議可防止丟失修改,並保證事務T是可恢復的,它不能保證可重復讀和不讀“臟”數據。
二級封鎖協議:一級封鎖協議加上事務T在讀取數據R之前必須先對其加S鎖,讀完 后即可釋S鎖。
二級封鎖協議可以防止丟失修改和讀“臟”數據
三級封鎖協議:一級封鎖協議加上事務T在讀取數據R之前必須先對其加S鎖,直到 事務結束才釋放。
三級封鎖協議可防止丟失修改、讀臟數據和不可重復讀。
事務的四個隔離級別
mysql中的隔離級別有四種:
封鎖的粒度
封鎖對象的大小稱為封鎖粒度(Granularity)
封鎖的對象:邏輯單元,物理單元
邏輯單元: 屬性值、屬性值的集合、元組、關系、索引項、整個索引、整個數據庫等
物理單元:頁(數據頁或索引頁)、物理記錄等
封鎖粒度與系統的並發度和並發控制的開銷密切相關。
封鎖的粒度越大,數據庫所能夠封鎖的數據單元就越少,並發度就越小,系統開銷也越小;
封鎖的粒度越小,並發度較高,但系統開銷也就越大
表鎖:封鎖粒度為表(關系)的鎖
行鎖:封鎖粒度為行(元祖)的鎖
悲觀鎖與樂觀鎖
(1)悲觀鎖:顧名思義,就是很悲觀,每次去拿數據的時候都認為別人會修改,所以每次在拿數據的時候都會上鎖,這樣別人想拿這個數據就會block直到它拿到鎖。傳統的關系型數據庫里邊就用到了很多這種鎖機制,比如行鎖,表鎖等,讀鎖,寫鎖等,都是在做操作之前先上鎖。
(2)樂觀鎖: 顧名思義,就是很樂觀,每次去拿數據的時候都認為別人不會修改,所以不會上鎖,但是在更新的時候會判斷一下在此期間別人有沒有去更新這個數據,可以使用版本號等機制。樂觀鎖適用於多讀的應用類型,這樣可以提高吞吐量,像數據庫如果提供類似於write_condition機制的其實都是提供的樂觀鎖。
(3)悲觀鎖 和 樂觀鎖的區別:
- 樂觀鎖在不發生取鎖失敗的情況下開銷比悲觀鎖小,但是一旦發生失敗回滾開銷則比較大,因此適合用在取鎖失敗概率比較小的場景,可以提升系統並發性能
- 樂觀鎖還適用於一些比較特殊的場景,例如在業務操作過程中無法和數據庫保持連接等悲觀鎖無法適用的地方