悲觀鎖(pessimistic lock), 顧名思義,就是很悲觀,每次去拿資料的時候都認為別人會修改,所以每次在拿資料的時候都會上鎖,這樣別人想拿這個資料就會block直到它拿到鎖。樂觀鎖(optimistic lock), 顧名思義,就是很樂觀,每次去拿資料的時候都認為別人不會修改,所以不會上鎖,但是在更新的時候會判斷一下在此期間別人有沒有去更新這個資料,可以使用版本號等機制。樂觀鎖適用於多讀的應用型別,這樣可以提高吞吐量,像資料庫如果提供類似於write_condition機制的其實都是提供的樂觀鎖。
結合上家公司的專案,回顧一下tair的悲觀鎖實現。tair有lock方法,但是該方法並不好用,所以多數都是使用帶版本號的put函式,實現鎖機制。注意:初始化時不能為0和1的,否則會出現衝突。
tair的悲觀鎖最簡單的實現如下:
/**
* 修改和增加快取中的資料
* @param key
* @param imsgmodel
* @return
*/ public boolean putmsgcache(serializable key, string value, int version)
} catch (exception e)
return false;
}
然而仔細推敲**,發現上述**存在乙個比較嚴重的問題:對某一key上鎖後,如果此時當前程序crash後,該key會永遠被鎖住。
要解決這個問題,乙個比較簡單粗暴的方法是增加乙個有效時間,讓該key自動失效。但是這個有效時間需要和業務相關,不太好控制。
所以將上述**,修改如下:
public boolean putmsgcache(serializable key, string value, int version, long delay)
} catch (exception e)
return false;
}
但是上述**還是有些缺陷,如果tair的put超時怎麼辦?
其實超時可以分為兩種情況:
1、tair已經將資料put成功,但是返回結果的時候超時
2、請求tair的put方法就已經超時,此時並沒有將資料put成功。
對於第一種,下次重試的時候,肯定會失敗。對於第二種,下次重試的時候,會成功。所以重點是如何解決第一類問題。
上述**都是對key和version做文章,這裡我們可以在value上做文章。
public boolean putmsgcache(serializable key, string value, int version, long delay)
}else if(result.getvalue() != null && getlockvalue().equals(result.getvalue().getvalue()))
return false;
}
所以該方法重點的任務是設計value,該value務必保證是重試的進來的。所以可以用使用uuid或者執行緒名+主機名的方法。
private string getlockvalue()
那麼問題又來了,如果get操作也超時怎麼辦了??
此時也沒有什麼好辦法,只有一次次重試了
所以,可以將上述程式修改如下:
public boolean trylock(string key, int timeout) .",key);
return false;
} if (resultcode.datanotexsits.equals(result.getrc()))
}else if(result.getvalue() != null && getlockvalue().equals(result.getvalue().getvalue()))
return false;
} private string getlockvalue()
注意此時只重試了一次,如果想保險起見,可以使用while迴圈,增加乙個重試次數。
version,版本控制,是樂觀鎖的根基。樂觀鎖並不是真的鎖,它只是加了乙個標識,用於區分資料是否有被意料之外的更改過。若沒有,那就正常提交事物結束操作;若有,則回滾事物,撤銷所有操作。所以,樂觀鎖除了需要應用於寫衝突很少的場景,還要求系統支援事物回滾或補償。樂觀鎖的大概流程如下:
1、獲取版本號
2、處理事務
3、檢查版本號是否更新(此時應該相當於悲觀鎖)
4、檢測成功,提交事務
5、檢測版本號失敗,回滾事務,調到1
所以樂觀鎖並不是乙個真正意義上的鎖,可以認為是乙個處理任務的邏輯。
樂觀鎖的優缺點很明確,適用於併發衝突少的環境,如果併發衝突情況特別頻繁的情況下,會不斷進入第5步,存在著大量的迴圈操作,處理該任務的執行緒一直得不到釋放,很消耗資源。
上述只是運用tair的時候總結,總之合理運用樂觀鎖和悲觀鎖即可
悲觀鎖和樂觀鎖的理解
查了一下悲觀鎖和樂觀鎖的目的主要是為了避免髒讀,看起來和oracle的預設隔離級別read commited是衝突的。因為read commited是可以避免髒讀的。細想一下,果然有問題。請看髒讀的定義 t1修改了記錄,還沒有提交,t2讀取了該記錄,t1回滾了修改。read commited能夠防止...
悲觀鎖和樂觀鎖
1.悲觀鎖,正如其名,它指的是對資料被外界 包括本系統當前的其他事務,以及來自外部系統的事務處理 修改持保守態度,因此,在整個資料處理過程中,將資料處於鎖定狀態。悲觀鎖的實現,往往依靠資料庫提供的鎖機制 也只有資料庫層提供的鎖機制才能真正保證資料訪問的排他性,否則,即使在本系統中實現了加鎖機制,也無...
悲觀鎖和樂觀鎖
前幾天有人問了我乙個問題,說如果資料庫某些操作不用事務,那麼又需要保持資料的一致性,那麼該用什麼方法替代事務。我就想到了悲觀鎖和樂觀鎖的思想,下面我解釋一下在資料庫中的悲觀鎖和樂觀鎖 1.悲觀鎖就是把資料庫的一些操作,放在事務當中,依賴資料庫的隔離級別,實現對資料修改的封鎖,這樣做資料一致性可以保持...