您好,登錄后才能下訂單哦!
這篇文章將為大家詳細講解有關數據庫需要鎖機制的原因以及鎖機制的種類是什么,文章內容質量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關知識有一定的了解。
為什么要鎖
數據庫是一個多用戶使用的共享資源,比如一個用戶表t_user,兩個瀏覽器前面的人登錄了同個一個賬號,把電話號碼改了。當多個用戶并發地存取數據時,在數據庫中就會產生多個事務同時存取同一數據的情況。若對并發操作不加控制就可能會讀取和存儲不正確的數據,破壞數據庫的一致性(臟讀,不可重復讀,幻讀等),可能產生死鎖。為了解決這個問題,加鎖是一個非常重要的技術,對實現數據庫并發控制是一個好的方案。簡單說,當一個執行sql語句的事務想要操作表記錄之前,先向數據庫發出請求,對你訪問的記錄集加鎖,在這個事務釋放這個鎖之前,其他事務不能對這些數據進行更新操作。
有哪些鎖
鎖包括行級鎖、表級鎖、悲觀鎖、樂觀鎖
1. 行級鎖:一種它鎖,防止另外事務修改此行;
在使用以下語句時,Oracle會自動應用行級鎖:
INSERT、UPDATE、DELETE、SELECT … FOR UPDATE [OF columns] [WAIT n | NOWAIT];SELECT … FOR UPDATE語句,允許用戶一次鎖定多條記錄進行更新.
使用commit或者rollback釋放鎖。
MySql的innodb存儲引擎默認是行級鎖。特點:開鎖大,加鎖慢;會出現死鎖;鎖定粒度最小,發生鎖沖突的概率最低,并發度也最高。適合于有大量按索引更新少量不同數據,同時又有并發查詢的應用,如一些在線事務處理系統。
2.表級鎖:5種
1)行共享 (ROW SHARE) – 禁止排他鎖定表,與行排他類似,區別是別的事務還可以在此表上加任何排他鎖。(除排他(exclusive)外)
2)行排他(ROW EXCLUSIVE) – 禁止使用排他鎖和共享鎖,其他事務依然可以并發地對相同數據表執行查詢,插入,更新,刪除操作,或對表內數據行加鎖的操作,但不能有其他的排他鎖(自身是可以的,沒發現有什么用)
3)共享鎖(SHARE) - 鎖定表,對記錄只讀不寫,多個用戶可以同時在同一個表上應用此鎖,在表沒有被任何DML操作時,多個事務都可加鎖,但只有在僅一個事務加鎖的情況下只有此事務才能對表更新;當表已經被更新或者指定要更新時(select for update),任何事務都不能加此鎖了。
4)共享行排他(SHARE ROW EXCLUSIVE) – 比共享鎖更多的限制,禁止使用共享鎖及更高的鎖,在表沒有被任何DML操作時,只有一個事務可以加鎖,可以更新,書上說別的事務可以使用select for update鎖定選中的數據行,可是實驗后沒被驗證。
5)排他(EXCLUSIVE) – 限制最強的表鎖,僅允許其他用戶查詢該表的行。禁止修改和鎖定表
行級鎖和表級鎖是根據鎖的粒度來區分的,行記錄,表都是資源,鎖是作用在這些資源上的。如果粒度比較小(比如行級鎖),可以增加系統的并發量但需要較大的系統開銷,會影響到性能,出現死鎖,,因為粒度小則操作的鎖的數量會增加;如果作用在表上,粒度大,開銷小,維護的鎖少,不會出現死鎖,但是并發是相當昂貴的,因為鎖定了整個表就限制了其它事務對這個表中其他記錄的訪問。
悲觀鎖:
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>
關于數據庫需要鎖機制的原因以及鎖機制的種類是什么就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。