樂觀鎖:數據庫
原理: 1)經過在數據庫表中添加一個版本號(version)字段來實現樂觀鎖。 2)更新前先獲取到該條數據的版本號(v1),而後在更新語句(更新數據&更新版本號)的where條件中添加 version=v1 條件, 1>若知足version=v1條件(即:成功獲取樂觀鎖),則成功更新數據且版本號+1; 2>若不知足version=v1條件(即:獲取樂觀鎖失敗),說明該條數據被其它線程修改過了,則更新失敗,回滾事務。 特色: 1)不發生獲取鎖失敗的狀況下,開銷比較小。 2)若獲取鎖失敗,則代碼須要回滾,開銷比較大。 應用: 樂觀鎖適用於鎖獲取失敗的機率比較小的場景,即:讀取比較頻繁、寫入較少的場景。 缺點: 只能保證本系統對數據(數據庫表)的操做是安全的,外部系統對數據(數據庫表)的操做是不可控的。 解決辦法:對外部系統設置權限,即外部系統只有普通查詢的權限。
悲觀鎖:安全
原理:使用數據庫提供的鎖機制(select .. for update)。 注意:使用悲觀鎖前,必須先關閉MySQL的自動提交屬性。 應用: 寫入比較頻繁的場景。
咱們應該儘可能避免使用長事務:session
1)在一個事務中執行批量操做(eg:循環插入數據、循環刪除數據等)會致使該事務的執行時間變長。 2)長事務會致使數據庫鏈接被長時間持有,若是該請求的併發量較高,則極可能出現鏈接池中的鏈接被用光的狀況,從而致使其它的請求(因沒法獲取到數據庫鏈接)一直處於等待狀態,沒法被響應。 3)咱們應該將事務的範圍控制在單個操做上。
Hibernate中的樂觀鎖和悲觀鎖:併發
概念:指數據庫的隔離級別設爲read committed時,爲了解決不可重複讀的問題而採用的兩種辦法: 1)設定hibernate的事務隔離級別(使用hibernate.connection.isolation配置:取值一、二、四、8) hibernate.connection.isolation = 2(若是不設,則默認依賴數據庫自己的級別) 2)採用樂觀鎖和悲觀鎖解決不可重複讀的問題 1)悲觀鎖:使用另外一種load方法:load(xx.class , id , LockMode.Upgrade),把讀出來的數據加上一把鎖,在事務結束前別人沒法訪問,須要藉助數據庫中的鎖。 注:LockMode.UPGRADE_NOWAIT是 ORACLE 支持的鎖的方式 2)樂觀鎖:在程序中添加一個version字段,用來檢查是否被修改過。版本檢查使用版本號或者時間戳來檢測更新衝突(而且防止更新丟失)。 在實體類中增長version屬性(數據庫也會對應生成該字段,初始值爲0),並在其get方法前加@Version註解,則在操做過程當中每更新一次該行數據則version值加1,便可在事務提交前判斷該數據是否被其餘事務修改過。 eg: 悲觀鎖(PessimisticLock): public void testPessimisticLock() { Session session = sf.openSession(); session.beginTransaction(); // LockMode.UPGRADE 的意思就是:在讀這條記錄的時候,請數據庫爲我讀的這條記錄加把鎖 Account a = (Account)session.load(Account.class, 1L, LockMode.UPGRADE); int balance = a.getBalance(); //do some caculation balance = balance - 10; a.setBalance(balance); session.getTransaction().commit(); session.close(); } 控制檯發出的SQL語句:select ... for update 分析:在select語句後加上了for update,說明這裏在數據庫中加了一把鎖。