資料庫中的悲觀鎖和樂觀鎖詳解,資料庫詳解
資料中的鎖分為兩類:悲觀鎖和樂觀鎖,鎖還有表級鎖、行級鎖
表級鎖例如:
SELECT * FROM table WITH (HOLDLOCK) 其他事務可以讀取表,但不能更新刪除
SELECT * FROM table WITH (TABLOCKX) 其他事務不能讀取表,更新和刪除
行級鎖例如:
select * from table_name where id = 1 for update;
悲觀鎖(Pressimistic Locking)
對資料被外界(包括本系統當前的其他事務,以及來自
外部系統的交易處理)修改持保守態度,因此,在整個資料處理過程中,將資料處於鎖定狀態。悲觀鎖的實現,往往依靠資料庫提供的鎖機制(也只有資料庫層提供的鎖機制才能真正保證資料訪問的排他性,否則,即使在本系統中實現了加鎖機制,也無法保證外部系
統不會修改資料)。例如:
select * from table_name where id = ‘xxx’ for update;
這樣查詢出來的這一行資料就被鎖定了,在這個update事務提交之前其他外界是不能修改這條資料的,但是這種處理方式效率比較低,一般不推薦使用。
樂觀鎖(Optimistic Locking)
相對悲觀鎖而言,樂觀鎖機制採取了更加寬鬆的加鎖機制。悲觀鎖大多數情況下依靠資料庫的鎖機制實現,以保證操作最大程度的獨佔性。但隨之而來的就是資料庫效能的大量開銷,特別是對長事務而言,這樣的開銷往往無法承受。如一個金融系統,當某個操作員讀取使用者的資料,並在讀出的使用者資料的基礎上進行修改時(如更改使用者帳戶餘額),如果採用悲觀鎖機制,也就意味著整個操作過程中(從操作員讀出資料、開始修改直至提交修改結果的全過程,甚至還包括操作員中途去煮咖啡的時間),資料庫記錄始終處於加鎖狀態,可以想見,如果面對幾百上千個並發,這樣的情況將導致怎樣的後果。
樂觀鎖機制在一定程度上解決了這個問題。樂觀鎖,大多是基於資料版本( Version )記錄機制實現。何謂資料版本?即為資料增加一個版本標識,在基於資料庫表的版本解決方案中,一般是通過為資料庫表增加一個 “version” 欄位來實現。讀取出資料時,將此版本號碼一同讀出,之後更新時,對此版本號碼加一。此時,將提交資料的版本資料與資料庫表對應記錄的目前的版本資訊進行比對,如果提交的資料版本號碼大於資料庫表目前的版本號,則予以更新,否則認為是到期資料。
舉個樂觀鎖的例子(資料庫version預設為0):
不如現在一件衣服就剩一個庫存了,但是有兩個使用者同時下單,如果這時候不加以控制很容易出現庫存賣超的情況,這時候我們可以這樣操作:
第一個使用者將這件衣服讀出(version=0),並將庫存-1,
第二個使用者也將這件衣服讀出(version=0),並將庫存-1,
第一個個使用者完成操作,將資料庫版本version+1,執行更新庫存時由於提交的資料版本大於資料庫記錄的版本,資料被更新,資料庫中的version被更新為2。
update goods set store=store-1,version=version+1 where id=xx and version=orginal_version
第二個使用者也完成了操作,也將版本version+1,執行更新庫存時發現執行版本和資料庫記錄的版本相同,不符合提交版本必須大於資料庫記錄版本的樂觀鎖策略,所以第二個使用者的下單請求被駁回,我們可以通過人性化處理異常給使用者提示該商品已售罄等。
樂觀鎖機制避免了長事務中的資料庫加鎖開銷(兩個使用者操作過程中,都沒有對資料庫資料加鎖),大大提升了大並發量下的系統整體效能表現。
悲觀鎖:交給資料庫來處理的,由事務(分隱私和明確交易,平時單條SQL語句就是一個隱含交易)+鎖 那控制的,其中事務相當於鎖的範圍,根據事務的提交失敗或復原來釋放掉明確交易中開啟的鎖。(事前處理)
樂觀鎖:是認為版本號碼來控制的,這種機制並發性和效能更好(事後處理)