一、樂觀鎖和悲觀鎖
數(shù)據(jù)庫實現(xiàn)并發(fā)控制的手段一般分為樂觀鎖和悲觀鎖。
悲觀鎖:
? ? ? 當(dāng)我們要對一個數(shù)據(jù)庫中的一條數(shù)據(jù)進(jìn)行修改的時候,為了避免同時被其他人修改,最好的辦法就是直接對該數(shù)據(jù)進(jìn)行加鎖以防止并發(fā)。這種借助數(shù)據(jù)庫鎖機(jī)制,在修改數(shù)據(jù)之前先鎖定,再修改的方式被稱之為悲觀并發(fā)控制(又名“悲觀鎖”,Pessimistic Concurrency Control,縮寫“PCC”)。
? ? ? 之所以叫做悲觀鎖,是因為這是一種對數(shù)據(jù)的修改抱有悲觀態(tài)度的并發(fā)控制方式。我們一般認(rèn)為數(shù)據(jù)被并發(fā)修改的概率比較大,所以需要在修改之前先加鎖。但是在效率方面,處理加鎖的機(jī)制會讓數(shù)據(jù)庫產(chǎn)生額外的開銷,還有增加產(chǎn)生死鎖的機(jī)會。另外還會降低并行性,一個事務(wù)如果鎖定了某行數(shù)據(jù),其他事務(wù)就必須等待該事務(wù)處理完才可以處理那行數(shù)據(jù)。
樂觀鎖:
樂觀鎖( Optimistic Locking ) 是相對悲觀鎖而言的,樂觀鎖假設(shè)數(shù)據(jù)一般情況下不會造成沖突,所以在數(shù)據(jù)進(jìn)行提交更新的時候,才會正式對數(shù)據(jù)的沖突與否進(jìn)行檢測,如果發(fā)現(xiàn)沖突了,則返回給用戶錯誤的信息,讓用戶決定如何去做。
樂觀并發(fā)控制相信事務(wù)之間的數(shù)據(jù)競爭(data race)的概率是比較小的,因此盡可能直接做下去,直到提交的時候才去鎖定,所以不會產(chǎn)生任何鎖和死鎖。
悲觀鎖實現(xiàn)方式
悲觀鎖的實現(xiàn),往往依靠數(shù)據(jù)庫提供的鎖機(jī)制。在數(shù)據(jù)庫中,悲觀鎖的流程如下:
在對記錄進(jìn)行修改前,先嘗試為該記錄加上排他鎖(exclusive locking)。
如果加鎖失敗,說明該記錄正在被修改,那么當(dāng)前查詢可能要等待或者拋出異常。具體響應(yīng)方式由開發(fā)者根據(jù)實際需要決定。
如果成功加鎖,那么就可以對記錄做修改,事務(wù)完成后就會解鎖了。
其間如果有其他對該記錄做修改或加排他鎖的操作,都會等待我們解鎖或直接拋出異常。
樂觀鎖實現(xiàn)方式
使用樂觀鎖就不需要借助數(shù)據(jù)庫的鎖機(jī)制了。
樂觀鎖的概念中其實已經(jīng)闡述了它的具體實現(xiàn)細(xì)節(jié)。主要就是兩個步驟:沖突檢測和數(shù)據(jù)更新。其實現(xiàn)方式有一種比較典型的就是CAS(Compare and Swap)。
CAS是項樂觀鎖技術(shù),當(dāng)多個線程嘗試使用CAS同時更新同一個變量時,只有其中一個線程能更新變量的值,而其它線程都失敗,失敗的線程并不會被掛起,而是被告知這次競爭中失敗,并可以再次嘗試。
以上更新語句存在一個比較重要的問題,即傳說中的ABA問題。
比如說一個線程one從數(shù)據(jù)庫中取出庫存數(shù)3,這時候另一個線程two也從數(shù)據(jù)庫中取出庫存數(shù)3,并且two進(jìn)行了一些操作變成了2,然后two又將庫存數(shù)變成3,這時候線程one進(jìn)行CAS操作發(fā)現(xiàn)數(shù)據(jù)庫中仍然是3,然后one操作成功。盡管線程one的CAS操作成功,但是不代表這個過程就是沒有問題的。
有一個比較好的辦法可以解決ABA問題,那就是通過一個單獨(dú)的可以順序遞增的version字段。
除了version以外,還可以使用時間戳,因為時間戳天然具有順序遞增性。