一、悲觀鎖
? ? 1、排它鎖,當事務在操作數據時把這部分數據進行鎖定,直到操作完畢后再解鎖,其他事務操作才可操作該部分數據。這將防止其他進程讀取或修改表中的數據。
? ? 2、實現:大多數情況下依靠數據庫的鎖機制實現
? ? ?一般使用 select …for update 對所選擇的數據進行加鎖處理,例如select * from account where name=”Max” for update,?這條sql 語句鎖定了account 表中所有符合檢索條件(name=”Max”)的記錄。本次事務提交之前(事務提交時會釋放事務過程中的鎖),外界無法修改這些記錄。
二、樂觀鎖
? ? 1、如果有人在你之前更新了,你的更新應當是被拒絕的,可以讓用戶重新操作。
? ? 2、實現:大多數基于數據版本(Version)記錄機制實現
? ? ?具體可通過給表加一個版本號或時間戳字段實現,當讀取數據時,將version字段的值一同讀出,數據每更新一次,對此version值加一。當我們提交更新的時候,判斷當前版本信息與第一次取出來的版本值大小,如果數據庫表當前版本號與第一次取出來的version值相等,則予以更新,否則認為是過期數據,拒絕更新,讓用戶重新操作。
三、ORM框架中悲觀鎖樂觀鎖的應用
? ? ?一般悲觀鎖、樂觀鎖都需要都通過sql語句的設定、數據的設計結合代碼來實現,例如樂觀鎖中的版本號字段,單純面向數據庫操作,是需要自己來實現樂觀鎖的,簡言之,也就是版本號或時間戳字段的維護是程序自己維護的,自增、判斷大小確定是否更新都通過代碼判斷實現。數據庫進提供了樂觀、悲觀兩個思路進行并發控制。
? ? ?對于常用java 持久化框架,對于數據庫的這一機制都有自己的實現,以Hibernate為例,總結一下ORM框架中悲觀鎖樂觀鎖的應用
1、Hibernate的悲觀鎖:
? ? ?基于數據庫的鎖機制實現。如下查詢語句:
String?hqlStr?="from?TUser?as?user?where?user.name=Max"; Query?query?=?session.createQuery(hqlStr); query.setLockMode("user",LockMode.UPGRADE);?//加鎖 List?userList?=?query.list();//執行查詢,獲取數據
? ? ?觀察運行期Hibernate生成的SQL語句:
select?tuser0_.id?as?id,?tuser0_.name?as?name,?tuser0_.group_id?as?group_id,?tuser0_.user_type?as?user_type,? tuser0_.sex?as?sex?from?t_user?tuser0_?where?(tuser0_.name='Erica'?)?for?update
? ? ?這里Hibernate通過使用數據庫的for update子句實現了悲觀鎖機制。對返回的所有user記錄進行加鎖。
2、Hibernate的加鎖模式有:
? ? ?? LockMode.NONE : 無鎖機制。
? ? ?? LockMode.WRITE :Hibernate在寫操作(Insert和Update)時會自動獲取寫鎖。
? ? ?? LockMode.READ : Hibernate在讀取記錄的時候會自動獲取。
? ? ?這三種鎖機制一般由Hibernate內部使用,如Hibernate為了保證Update過程中對象不會被外界修改,會在save方法實現中自動為目標對象加上WRITE鎖。
? ? ?? LockMode.UPGRADE :利用數據庫的for update子句加鎖。
? ? ?? LockMode. UPGRADE_NOWAIT :Oracle的特定實現,利用Oracle的for update nowait子句實現加鎖。
? ? ?注意,只有在查詢開始之前(也就是Hiberate 生成SQL 之前)設定加鎖,才會真正通過數據庫的鎖機制進行加鎖處理,否則,數據已經通過不包含for update子句的Select SQL加載進來,所謂數據庫加鎖也就無從談起。
3、Hibernate的樂觀鎖
? ? ?Hibernate 在其數據訪問引擎中內置了樂觀鎖實現。如果不用考慮外部系統對數據庫的更新操作,利用Hibernate提供的透明化樂觀鎖實現,將大大提升我們的生產力。Hibernate中可以通過class描述符的optimistic-lock屬性結合version描述符指定。具體實現方式如下:
? ? ?現在,我們為之前示例中的TUser加上樂觀鎖機制。
實現一、 配置optimistic-lock屬性:
<hibernate-mapping> ?????<class> ???????????…… ?????</class></hibernate-mapping>
optimistic-lock屬性有如下可選取值:
? ? ?? none:無樂觀鎖
? ? ?? version:通過版本機制實現樂觀鎖
? ? ?? dirty:通過檢查發生變動過的屬性實現樂觀鎖
? ? ?? all:通過檢查所有屬性實現樂觀鎖
? ? ?通過version實現的樂觀鎖機制是Hibernate官方推薦的樂觀鎖實現,同時也是Hibernate中,目前唯一在數據對象脫離Session發生修改的情況下依然有效的鎖機制。因此,一般情況下,我們都選擇version方式作為Hibernate樂觀鎖實現機制。
實現二、添加一個Version屬性描述符
<hibernate-mapping> ?????<class>? <id><generator></generator></id><version></version> …… ?????</class></hibernate-mapping>
? ? ?注意version 節點必須出現在ID 節點之后。這里聲明了一個version屬性,用于存放用戶的版本信息,保存在TUser表的version字段中。
測試:
? ? ?此時如果我們嘗試編寫一段代碼,更新TUser表中記錄數據,如:
Criteria?criteria?=?session.createCriteria(TUser.class); criteria.add(Expression.eq("name","Max")); List?userList?=?criteria.list(); TUser?user?=(TUser)userList.get(0); Transaction?tx?=?session.beginTransaction(); user.setUserType(1);?//更新UserType字段 tx.commit();
? ? ?每次對TUser進行更新的時候,我們可以發現,數據庫中的version都在遞增。而如果我們嘗試在tx.commit 之前,啟動另外一個Session,對名為Max的用戶進行操作,下面模擬并發更新時的情況:
Session?session=?getSession(); Criteria?criteria?=?session.createCriteria(TUser.class); criteria.add(Expression.eq("name","Max")); Session?session2?=?getSession(); Criteria?criteria2?=?session2.createCriteria(TUser.class); criteria2.add(Expression.eq("name","Max")); List?userList?=?criteria.list(); List?userList2?=?criteria2.list();TUser?user?=(TUser)userList.get(0); TUser?user2?=(TUser)userList2.get(0); Transaction?tx?=?session.beginTransaction(); Transaction?tx2?=?session2.beginTransaction(); user2.setUserType(99); tx2.commit(); user.setUserType(1); tx.commit();
? ? ?執行并發更新的代碼,在tx.commit()處拋出StaleObjectStateException異常,并指出版本檢查失敗,當前事務正在試圖提交一個過期數據。通過捕捉這個異常,我們就可以在樂觀鎖校驗失敗時進行相應處理。
? ? ?這就是hibernate實現悲觀鎖和樂觀鎖的主要方式。
四、總結
? ? ?悲觀鎖相對比較謹慎,設想現實情況應該很容易就發生沖突,所以我還是獨占數據資源吧。
? ? ?樂觀鎖就想得開而且非常聰明,應該是不會有什么沖突的,我對表使用一個時間戳或者版本號,每次讀、更新操作都對這個字段進行比對,如果在我之前已經有人對數據進行更新了,那就讓它更新,大不了我再讀一次或者再更新一次。
? ? ?樂觀鎖的管理跟SVN管理代碼版本的原理很像,如果在我提交代碼之前用本地代碼的版本號與服務器做對比,如果本地版本號小于服務器上最新版本號,則提交失敗,產生沖突代碼,讓用戶決定選擇哪個版本繼續使用。
? ? ?在實際生產環境里邊,如果并發量不大且不允許臟讀,可以使用悲觀鎖;但如果系統的并發非常大的話,悲觀鎖定會帶來非常大的性能問題,所以我們就要選擇樂觀鎖定的方法 ? ? ? ?另外,Mysql在處理并發訪問數據上,還有添加讀鎖(共享鎖)、寫鎖(排它鎖),控制鎖粒度【表鎖(table lock)、行級鎖(row lock)】等實現,有興趣可以繼續研究。
?以上就是MySQL數據庫優化(三)—MySQL悲觀鎖和樂觀鎖(并發控制)的內容,更多相關內容請關注PHP中文網(www.php.cn)!