數據庫爲何須要鎖機制?有哪些鎖機制?

    【爲何要鎖】sql

    數據庫是一個多用戶使用的共享資源,好比一個用戶表t_user,兩個瀏覽器前面的人登陸了同個一個帳號,把電話號碼改了。當多個用戶併發地存取數據時,在數據庫中就會產生多個事務同時存取同一數據的狀況。若對併發操做不加控制就可能會讀取和存儲不正確的數據,破壞數據庫的一致性(髒讀,不可重複讀,幻讀等),可能產生死鎖。爲了解決這個問題,加鎖是一個很是重要的技術,對實現數據庫併發控制是一個好的方案。簡單說,當一個執行sql語句的事務想要操做表記錄以前,先向數據庫發出請求,對你訪問的記錄集加鎖,在這個事務釋放這個鎖以前,其餘事務不能對這些數據進行更新操做。數據庫

    【有哪些鎖】瀏覽器

     鎖包括行級鎖、表級鎖、悲觀鎖、樂觀鎖併發

     行級鎖:一種它鎖,防止另外事務修改此行;在使用如下語句時,Oracle會自動應用行級鎖:INSERT、UPDATE、DELETE、SELECT … FOR UPDATE [OF columns] [WAIT n | NOWAIT];SELECT … FOR UPDATE語句容許用戶一次鎖定多條記錄進行更新.使用commit或者rollback釋放鎖。MySql的innodb存儲引擎默認是行級鎖。特色:開鎖大,加鎖慢;會出現死鎖;鎖定粒度最小,發生鎖衝突的機率最低,併發度也最高。適合於有大量按索引更新少許不一樣數據,同時又有併發查詢的應用,如一些在線事務處理系統。oracle

 

    表級鎖:5種app

   行共享 (ROW SHARE) – 禁止排他鎖定表,與行排他相似,區別是別的事務還能夠在此表上加任何排他鎖。(除排他(exclusive)外)
   行排他(ROW EXCLUSIVE) – 禁止使用排他鎖和共享鎖,其餘事務依然能夠併發地對相同數據表執行查詢,插入,更新,刪除操做,或對錶內數據行加鎖的操做,但不能有其餘的排他鎖(自身是能夠的,沒發現有什麼用)
   共享鎖(SHARE) - 鎖定表,對記錄只讀不寫,多個用戶能夠同時在同一個表上應用此鎖,在表沒有被任何DML操做時,多個事務均可加鎖,但只有在僅一個事務加鎖的狀況下只有此事務才能對錶更新;當表已經被更新或者指定要更新時(select for update),任何事務都不能加此鎖了。
   共享行排他(SHARE ROW EXCLUSIVE) – 比共享鎖更多的限制,禁止使用共享鎖及更高的鎖,在表沒有被任何DML操做時,只有一個事務能夠加鎖,能夠更新,書上說別的事務可使用select for update鎖定選中的數據行,但是實驗後沒被驗證。
   排他(EXCLUSIVE) – 限制最強的表鎖,僅容許其餘用戶查詢該表的行。禁止修改和鎖定表性能

 

   行級鎖和表級鎖是根據鎖的粒度來區分的,行記錄,表都是資源,鎖是做用在這些資源上的。若是粒度比較小(好比行級鎖),能夠增長系統的併發量但須要較大的系統開銷,會影響到性能,出現死鎖,,由於粒度小則操做的鎖的數量會增長;若是做用在表上,粒度大,開銷小,維護的鎖少,不會出現死鎖,可是併發是至關昂貴的,由於鎖定了整個表就限制了其它事務對這個表中其餘記錄的訪問。hibernate

   悲觀鎖:索引

   Pessimistic Lock正如其名,它指的是對數據被外界(包括本系統當前的其餘事務,以及來自外部系統的事務處理)修改持保守悲觀態度,事務每次去操做數據的時候都假設有其餘事務會修改須要訪問的數據,因此在訪問以前都要求上鎖,行鎖,表鎖等,讀鎖,寫鎖等,都是在作操做以前先上鎖,所以,在整個數據處理過程當中,將數據處於鎖定狀態。悲觀鎖的實現,每每依靠數據庫提供的鎖機制(也只有數據庫層提供的鎖機制才能 真正保證數據訪問的排他性,不然,即便在本系統中實現了加鎖機制,也沒法保證外部系 統不會修改數據)。 一個典型的倚賴數據庫的悲觀鎖調用: select * from account where name=」Erica」 for update 這條sql 語句鎖定了account 表中全部符合檢索條件(name=」Erica」)的記錄。 本次事務提交以前(事務提交時會釋放事務過程當中的鎖),外界沒法修改這些記錄。事務

    Hibernate悲歡鎖實現:基於數據庫鎖機制

         Query q=Session.createQuery("select  * from t_profit where amount>10000");

          q.setLockMode("Profit",LockMode.UPGRADE);//Profit是Profit類的別名

         List<Profit> ps=q.list();

       執行的sql:select ....from t_profit where amount>10000 for update.hibernate的悲觀鎖經過數據庫的for update實現。

     LockMode.NONE:無鎖機制;

     LockMode.WRITE:insert,update記錄時自動獲取悲觀鎖;

     LockMode.READ在讀取時自動獲取悲觀鎖;

     LockMode.UPGRADE:利用數據庫的for update子句加鎖;

     LockMode.UPGRADE_NOWAIT:oracle特定實現,用oracle的for update nowait子句加鎖

  樂觀鎖:

   Optimistic Lock,和悲歡鎖相反,事務每次去操做數據以前,都假設其餘事務不會修改這些須要訪問的數據 ,因此 在訪問以前不要求上鎖,只是在進行更新修改操做的時候判斷一下在訪問的期間有沒有其餘人修改數據 了。它適用於多讀的應用類型,衝突真的發生比較少的時候就比較好,這樣省去了開銷的開銷,能夠提升吞吐量;但若是是真的常常要發生衝突的,那每次還要去判斷進行retry,反倒下降的性能,這個時候悲歡鎖比較好。數據庫若是提供相似於write_condition機制的其實都是提供的樂觀鎖。

          它的實現大可能是基於數據版本versin記錄機制。舉個例子:

       1.利潤表t_profit中有一個 version字段,當前值爲1;而總資產餘額字段(balance)爲$10000

       2.操做員A讀出version=1,從總資產減除2000,10000-2000=8000.

       3.A還沒操做結束,此時操做員B也讀出version=1,總資產減除5000,10000-5000=5000.

       4.A操做完成,把version加1,修改成2,把總資產減2000後提交更新數據庫,更新成功

       5.B操做了,也加version加1,修改成2,把總資產減5000後提交更新數據庫,此時發現version已經爲2了,如B修改後加1的version同樣,不知足樂觀鎖策略:"提交的版本必有大於記錄當前的版本才能執行"。所以B的操做請求被駁回,這樣就避免了B就version=1的舊數據修改的結果覆蓋了A操做的結果的可能。如沒有樂觀鎖,那A減去2000後剩餘8000,但B操做的時候是用10000-5000剩餘5000的,若是B的提交成功,總資產餘額就是5000,但實際狀況應該是8000-5000=3000的。出現總資產表記錄和實際支出不一致。

    Hibernate對樂觀鎖的實現:

      <hibernate-mapping>

        <class name="com.f.TProfit" table="t_profit" optimistic-lock="version"></class>

      </hibernate-mapping>

相關文章
相關標籤/搜索