日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

数据库乐观锁和悲观锁的理解和实现(转载amp;总结)

發布時間:2023/12/20 数据库 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 数据库乐观锁和悲观锁的理解和实现(转载amp;总结) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

數據的鎖定分為兩種,第一種叫作悲觀鎖,第二種叫作樂觀鎖。

1、悲觀鎖,就是對數據的沖突采取一種悲觀的態度,也就是說假設數據肯定會沖突,所以在數據開始讀取的時候就把數據鎖定住。【數據鎖定:數據將暫時不會得到修改】

2、樂觀鎖,認為數據一般情況下不會造成沖突,所以在數據進行提交更新的時候,才會正式對數據的沖突與否進行檢測,如果發現沖突了,則讓用戶返回錯誤的信息。讓用戶決定如何去做。


理解:

1. 樂觀鎖是一種思想,具體實現是,表中有一個版本字段,第一次讀的時候,獲取到這個字段。處理完業務邏輯開始更新的時候,需要再次查看該字段的值是否和第一次的一樣。如果一樣更新,反之拒絕。

之所以叫樂觀,因為這個模式沒有從數據庫加鎖。

2. 悲觀鎖是讀取的時候為后面的更新加鎖,之后再來的讀操作都會等待。這種是數據庫鎖

樂觀鎖優點程序實現,不會存在死鎖等問題。他的適用場景也相對樂觀。阻止不了除了程序之外的數據庫操作。

悲觀鎖是數據庫實現,他阻止一切數據庫操作。

再來說更新數據丟失,所有的讀鎖都是為了保持數據一致性。樂觀鎖如果有人在你之前更新了,你的更新應當是被拒絕的,可以讓用戶從新操作。悲觀鎖則會等待前一個更新完成。這也是區別。具體業務具體分析


實現:

一、悲觀鎖
? ? 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的悲觀鎖:

? ? ?基于數據庫的鎖機制實現。如下查詢語句:

[html]?view plaincopy
  • 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語句:
    [html]?view plaincopy
  • 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屬性:

    [html]?view plaincopy
  • <hibernate-mapping>??
  • ?????<class?name="org.hibernate.sample.TUser"?table="t_user"?dynamic-update="true"?dynamic-insert="true"?optimistic-lock="version">??
  • ???????????……??
  • ?????</class>??
  • </hibernate-mapping>??
  • optimistic-lock屬性有如下可選取值:
    ? ? ?? none: 無樂觀鎖
    ? ? ?? version: 通過版本機制實現樂觀鎖
    ? ? ?? dirty: 通過檢查發生變動過的屬性實現樂觀鎖
    ? ? ?? all: 通過檢查所有屬性實現樂觀鎖

    ? ? ?通過version實現的樂觀鎖機制是Hibernate官方推薦的樂觀鎖實現,同時也是Hibernate中,目前唯一在數據對象脫離Session發生修改的情況下依然有效的鎖機制。因此,一般情況下,我們都選擇version方式作為Hibernate樂觀鎖實現機制。
    實現二、添加一個Version屬性描述符

    [html]?view plaincopy
  • <hibernate-mapping>??
  • ?????<class?name="org.hibernate.sample.TUser"?table="t_user"???dynamic-update="true"?dynamic-insert="true"?optimistic-lock="version">???
  • ????<id?name="id"?column="id"?type="java.lang.Integer">??
  • ????????<generator?class="native"/>??
  • ????</id>??
  • ????<version?column="version"?name="version"?type="java.lang.Integer"/>??
  • ……??
  • ?????</class>??
  • </hibernate-mapping>??
  • ? ? ?注意version 節點必須出現在ID 節點之后。 這里聲明了一個version屬性,用于存放用戶的版本信息,保存在TUser表的 version字段中。

    測試:

    ? ? ?此時如果我們嘗試編寫一段代碼,更新TUser表中記錄數據,如:

    [html]?view plaincopy
  • 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的用 戶進行操作,下面模擬并發更新時的情況:
    [html]?view plaincopy
  • 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)】等實現,有興趣可以繼續研究。

    總結

    以上是生活随笔為你收集整理的数据库乐观锁和悲观锁的理解和实现(转载amp;总结)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。