樂觀鎖( Optimistic Locking ) 相對悲觀鎖而言,樂觀鎖假設認爲數據通常狀況下不會形成衝突,因此在數據進行提交更新的時候,纔會正式對數據的衝突與否進行檢,樂觀鎖適用於多讀的應用類型,這樣能夠提升吞吐量,像數據庫若是提供相似於write_condition機制的其實都是提供的樂觀鎖。相似SVNmysql
悲觀鎖假定其餘用戶企圖訪問或者改變你正在訪問、更改的對象的機率是很高的,所以在悲觀鎖的環境中,在你開始改變此對象以前就將該對象鎖住,而且直到你提交了所做的更改以後才釋放鎖。sql
1.使用數據版本(Version)記錄機制實現,這是樂觀鎖最經常使用的一種實現方式。何謂數據版本?即爲數據增長一個版本標識,通常是經過爲數據庫表增長一個數字類型的 「version」 字段來實現。當讀取數據時,將version字段的值一同讀出,數據每更新一次,對此version值加一。當咱們提交更新的時候,判斷數據庫表對應記錄的當前版本信息與第一次取出來的version值進行比對,若是數據庫表當前版本號與第一次取出來的version值相等,則予以更新,不然認爲是過時數據數據庫
2.樂觀鎖定的第二種實現方式和第一種差很少,一樣是在須要樂觀鎖控制的table中增長一個字段,名稱無所謂,字段類型使用時間戳(timestamp), 和上面的version相似,也是在更新提交的時候檢查當前數據庫中數據的時間戳和本身更新前取到的時間戳進行對比,若是一致則OK,不然就是版本衝突編程
悲觀的缺陷是不管是頁鎖仍是行鎖,加鎖的時間可能會很長,這樣可能會長時間的限制其餘用戶的訪問,也就是說悲觀鎖的併發訪問性很差。服務器
樂觀鎖則認爲其餘用戶企圖改變你正在更改的對象的機率是很小的,所以樂觀鎖直到你準備提交所做的更改時纔將對象鎖住,當你讀取以及改變該對象時並不加鎖。可見樂觀鎖加鎖的時間要比悲觀鎖短,樂觀鎖能夠用較大的鎖粒度得到較好的併發訪問性能。併發
好比:若是第二個用戶剛好在第一個用戶提交更改以前讀取了該對象,那麼當他完成了本身的更改進行提交時,數據庫就會發現該對象已經變化了,這樣,第二個用戶不得不從新讀取該對象並做出更改。在樂觀鎖環境中,會增長併發用戶讀取對象的次數。mvc
版本控制系統高併發
若是數據是可變的,而且沒法隔離呢?這種狀況下最經常使用的兩種控制就是樂觀併發控制和悲觀併發控制。性能
假設小張和小李想要同時修改同一個文件。若是使用樂觀鎖,倆人都能打開文件進行修改,若是小張先提交了內容,沒有問題,他所作的改變會保存到服務器上。但小李提交時就會遇到麻煩,版本控制服務器會檢測出兩種修改的衝突,小李的提交會被具體,並由小李決定該如何處理這種狀況(對於絕大部分版本控制軟件來講,會讀取並標識出小張作的改變,而後由小李決定是否合併)。 版本控制
若是使用的是悲觀鎖,小張先檢出(check out)文件,那麼小李就沒法再次檢出同一文件,直到小張提交了他的改變
將樂觀鎖想成一種檢測衝突的手段,而悲觀鎖是一種避免衝突的手段(嚴格來講,樂觀鎖其實不能稱之爲「鎖」,可是這個名字已經流傳開了,那就繼續使用吧) ,樂觀鎖能夠提升併發訪問的效率,可是若是出現了衝突只能向上拋出,而後重來一遍
悲觀鎖能夠避免衝突的發生,可是會下降效率,選擇使用那一種鎖取決於訪問頻率和一旦產生衝突的嚴重性
若是系統被併發訪問的機率很低,或者衝突發生後的後果不太嚴重(所謂後果應該指被檢測到衝突的提交會失敗,必須重來一次),可使用樂觀鎖,不然使用悲觀鎖
樂觀鎖的侷限是隻能在提交數據時才發現業務事務將要失敗,並且在某些狀況下,發現失敗太遲的代價會很大。一個方法是使用悲觀鎖,它能夠儘早地發現錯誤,但理難以編程實現,並且會下降系統的靈活性
MySQL InnoDB存儲引擎,實現的是基於多版本的併發控制協議——MVCC (Multi-Version Concurrency Control) (注:與MVCC相對的,是基於鎖的併發控制,Lock-Based Concurrency Control)。MVCC最大的好處,相信也是耳熟能詳:讀不加鎖,讀寫不衝突。在讀多寫少的OLTP應用中,讀寫不衝突是很是重要的,極大的增長了系統的併發性能,這也是爲何現階段,幾乎全部的RDBMS,都支持了MVCC
在InnoDB中,會在每行數據後添加兩個額外的隱藏的值來實現MVCC,這兩個值一個記錄這行數據什麼時候被建立,另一個記錄這行數據什麼時候過時(或者被刪除)。 在實際操做中,存儲的並非時間,而是事務的版本號,每開啓一個新事務,事務的版本號就會遞增。 在可重讀Repeatable reads事務隔離級別下:
經過MVCC,雖然每行記錄都須要額外的存儲空間,更多的行檢查工做以及一些額外的維護工做,但能夠減小鎖的使用,大多數讀操做都不用加鎖,讀數據操做很簡單,性能很好,而且也能保證只會讀取到符合標準的行,也只鎖住必要行
在MVCC併發控制中,讀操做能夠分紅兩類:快照讀 (snapshot read)與當前讀 (current read)。快照讀,讀取的是記錄的可見版本 (有多是歷史版本),不用加鎖。當前讀,讀取的是記錄的最新版本,而且,當前讀返回的記錄,都會加上鎖,保證其餘事務不會再併發修改這條記錄。
在一個支持MVCC併發控制的系統中,哪些讀操做是快照讀?哪些操做又是當前讀呢?以MySQL InnoDB爲例:
快照讀:簡單的select操做,屬於快照讀,不加鎖。(固然,也有例外,下面會分析)
當前讀:特殊的讀操做,插入/更新/刪除操做,屬於當前讀,須要加鎖。
全部以上的語句,都屬於當前讀,讀取記錄的最新版本。而且,讀取以後,還須要保證其餘併發事務不能修改當前記錄,對讀取記錄加鎖。其中,除了第一條語句,對讀取記錄加S鎖 (共享鎖)外,其餘的操做,都加的是X鎖 (排它鎖)。