實際生產環境裏邊,若是併發量不大,徹底可使用悲觀鎖定的方法,這種方法使用起來很是方便和簡單。可是若是系統的併發很是大的話,悲觀鎖定會帶來很是大的性能問題,因此就要選擇樂觀鎖定的方法。html
悲觀鎖假定其餘用戶企圖訪問或者改變你正在訪問、更改的對象的機率是很高的,所以在悲觀鎖的環境中,在你開始改變此對象以前就將該對象鎖住,而且直到你提交了所做的更改以後才釋放鎖。悲觀的缺陷是不管是頁鎖仍是行鎖,加鎖的時間可能會很長,這樣可能會長時間的限制其餘用戶的訪問,也就是說悲觀鎖的併發訪問性很差。jquery
樂觀鎖則認爲其餘用戶企圖改變你正在更改的對象的機率是很小的,所以樂觀鎖直到你準備提交所做的更改時纔將對象鎖住,當你讀取以及改變該對象時並不加鎖。可見樂觀鎖加鎖的時間要比悲觀鎖短,樂觀鎖能夠用較大的鎖粒度得到較好的併發訪問性能。可是若是第二個用戶剛好在第一個用戶提交更改以前讀取了該對象,那麼當他完成了本身的更改進行提交時,數據庫就會發現該對象已經變化了,這樣,第二個用戶不得不從新讀取該對象並做出更改。這說明在樂觀鎖環境中,會增長併發用戶讀取對象的次數。程序員
以版本控制系統爲例,來講說兩種最基本的併發性問題。web
【丟失更新】數據庫
小張想修改源代碼裏面的a方法,正在她修改的同時,小李打開了這個文件,修改了b方法而且保存了文件,等小張修改完成後,保存文件,小李所作的修改就被覆蓋了。編程
【不一致的讀】服務器
小張想要知道包裏面一共有多少個類,包分了a,b兩個子包。小張打開a包,看到了7個類。忽然小張接到老婆打來的電話,在小張接電話的時候,小李往a包中加了2個類,b包中加了3個類(原先b包中是5個類)。session
小張接完電話後再打開b包,看到了8個類,很天然得出結論:包中一共有15個類。併發
很遺憾,15個永遠不是正確的答案。在小李修改前,正確答案是12(7+5),修改後是17(9+8)。這兩個答案都是正確的,雖然有一個不是當前的。但15不對,由於小張讀取的數據是不一致的。高併發
小結:不一致讀指你要讀取兩種數據,這兩種數據都是正確的,可是在同一時刻二者並不是都正確。
【隔離 和 不可變】
在企業應用中,解決併發衝突的兩種經常使用手段是隔離和不可變。
只有當多個活動(進程或者線程)同時訪問同一數據時纔會引起併發問題。一種很天然的思路就是同一時刻只容許一個活動訪問數據。若是小張打開了文件,就不容許其餘人打開,或者其餘人只能經過只讀的方式打開副本,就能夠解決這個問題。
隔離可以有效減小發生錯誤的可能。咱們常常見到程序員陷入到併發問題的泥潭裏,每一段代碼寫完都要考慮併發問題,這樣太累了。咱們能夠利用隔離技術建立出隔離區域,當程序進入隔離區域時不用關心併發問題。好的併發性設計就是創造這樣的一些隔離區域,並保證代碼儘量的運行在其中。
另外一種思路:只有當你須要修改共享的數據時纔可能引起併發性問題,因此咱們能夠將要共享的數據製做爲「不可變」的,以免併發性問題。固然咱們不可能將全部的數據都作成不可變的,但若是一些數據是不可變的,對它們進行併發操做時咱們就能夠放鬆本身的神經了。
【樂觀併發控制、悲觀併發控制】
若是數據是可變的,而且沒法隔離呢?這種狀況下最經常使用的兩種控制就是樂觀併發控制和悲觀併發控制。
假設小張和小李想要同時修改同一個文件。若是使用樂觀鎖,倆人都能打開文件進行修改,若是小張先提交了內容,沒有問題,他所作的改變會保存到服務器上。但小李提交時就會遇到麻煩,版本控制服務器會檢測出兩種修改的衝突,小李的提交會被具體,並由小李決定該如何處理這種狀況(對於絕大部分版本控制軟件來講,會讀取並標識出小張作的改變,而後由小李決定是否合併)。
若是使用的是悲觀鎖,小張先檢出(check out)文件,那麼小李就沒法再次檢出同一文件,直到小張提交了他的改變。
建議你將樂觀鎖想成一種檢測衝突的手段,而悲觀鎖是一種避免衝突的手段(嚴格來講,樂觀鎖其實不能稱之爲「鎖」,可是這個名字已經流傳開了,那就繼續使用吧)。一些老的版本控制系統,好比VSS 6.0使用的是悲觀鎖的機制。而現代的版本控制系統通常兩種都支持,默認使用樂觀鎖。
兩種鎖各有優缺點。。。這段懶的翻譯了,很明顯看出,樂觀鎖能夠提升併發訪問的效率,可是若是出現了衝突只能向上拋出,而後重來一遍;悲觀鎖能夠避免衝突的發生,可是會下降效率。
選擇使用那一種鎖取決於訪問頻率和一旦產生衝突的嚴重性。若是系統被併發訪問的機率很低,或者衝突發生後的後果不太嚴重(所謂後果應該指被檢測到衝突的提交會失敗,必須重來一次),可使用樂觀鎖,不然使用悲觀鎖。
【我再補充兩句】咱們常常會在訪問數據庫的時候用到鎖,怎麼實現樂觀鎖和悲觀鎖呢?以Hibernate爲例,能夠經過爲記錄添加版本或時間戳字段來實現樂觀鎖。能夠用session.Lock()鎖定對象來實現悲觀鎖(本質上就是執行了SELECT * FROM t FOR UPDATE語句)。
另外一個高併發控制解決方案樂觀併發控制和悲觀併發控制總結概述:
咱們可使用兩種形式的併發控制策略:樂觀併發控制和悲觀併發控制。
假設martin和David同時都要編輯Customer文件。若是使用樂觀鎖策略,他們兩我的都能獲得一份文件的Copy,而且
能夠自由編輯文件。假設David第一個完成了工做,那麼他能夠毫無困難地更新他的修改。可是,當Martin想要提交他的修改時,併發控制策略就會開始起做用。源代碼控制系統會檢測到在Martin的修改與David的修改之間存在着衝突,於是拒絕Martin的提交,並由Martin負責指出怎樣處理這種狀況。若是使用悲觀鎖策略,只要有人先取出文件,其餘人就不能對該文件進行編輯。所以,假如是Martin先取了文件,那麼David就只能在Martin完成任務並提交以後才能對該文件進行操做。
若是把樂觀鎖看做是關於衝突檢測的,那麼悲觀鎖就是關於衝突避免的。在實際應用的源代碼控制系統中,
這兩種策略均可以被使用,可是如今大多數源代碼開發者更傾向於使用樂觀鎖策略。(有一種頗有道理的說法:樂觀鎖並非真正的鎖定,可是這種叫法很方便而且普遍流傳,以致於不容忽略。)
這兩種策略各有優缺點。悲觀鎖的問題是減小了併發的程序。當Martin正對一個被他加鎖的文件進行編輯的時候,
其它人只能等着。使用過悲觀的源代碼控制人都知道這是一種多麼使人喪氣的事情。對於企業數據,狀況常常會變得更加糟糕,只要有人在編輯,其餘人就沒法進行讀取,更加說進行編輯了。
樂觀鎖策略則容許人們更自由一些,由於只有在提交的時候纔有可能遇到阻礙。該策略的問題在於當衝突的時候會發生什麼樣的事情呢?事實上,David以後的全部人在提交的時候都必須讀取David修改過的那個版本,並指出怎樣合併本身和David的修改,而後再提交一個從新修改過的最新版本。有了源代碼控制系統,這樣作並不會有什麼麻煩。在許多場合下,源代碼控制系統確實可以自動進行合併操做,甚至在沒法自動合併的時候,也能讓使用都很容易看出不一樣文件版本之間的差異。可是,業務數據一般都是很難被自動合併的,因此常常只能扔掉原來的東西,而後從頭開始。
在樂觀鎖和悲觀鎖之間進行選擇的標準是:衝突的頻率與嚴重性。若是衝突不多,或者衝突的後果不會很嚴重,那麼一般狀況下應該選擇樂觀鎖,由於它能獲得更好的併發性,並且更容易實現。可是,若是衝突的結果對於用戶來講痛苦的,那麼就須要使用悲觀策略。
樂觀鎖的侷限是:只能在提交數據時才發現業務事務將要失敗,並且在某些狀況下,發現失敗太遲的代價會很大。用戶可能花了一個小時的時間輸入一份租約的詳細信息,錯誤太多會讓用戶對系統失去信心。另外一個方法是使用悲觀鎖,它能夠儘早地發現錯誤,但理難以編程實現,並且會下降系統的靈活性。
(注:以上是對併發控制中的樂觀鎖策略和悲觀鎖策略概念及解決思路的文字描述,下面我將對項目中具體怎麼實現樂觀鎖策略及悲觀鎖策略進行描述。)
樂觀鎖策略實現方法:
就是用C#中或SQL中的事務來實現數據操做不成功就回滾,我的感受火車站賣票系統也是這樣操做的,咱們看到顯示屏上有少許剩餘票,但咱們去買又打不出來。
悲觀鎖策略實現方法:
一、普通的aspx頁面,當用戶點提交後,直接將提交及相關按鈕的enabel改成false,直到提交事件完成後,再改回來。另外在數據層那一塊,每次提交數據更改時,都須要判斷數據之前的狀態是否改變,以防止有併發改變的狀況出現。
二、jquery中,在jquery中,能夠設置一個全局變量,提交時,先判斷全局變量狀態,如不容許提交則直接返回,如容許提交時,則先將全局變量置爲「不容許提交」,後開始提交,提交完成後,在jquery的post方法的callback方法中,再將全局變量改成「容許提交」。
三、彈出式窗口修改頁面,則用模態方式彈出,如web頁面中,可用window.showModalDialog()來實現模態方式打開修改頁面,來確保始終只有一個修改頁面被打開。(這是從數據操做頁面處就悲觀鎖定了數據,而不是在數據庫裏面悲觀鎖定)
原文標題:在數據庫中,併發控制有樂觀鎖和悲觀鎖之間,何時用樂觀鎖比較好何時用悲觀鎖比較好?
連接:http://www.cnblogs.com/chenlulouis/archive/2010/08/17/1801358.html