資料庫篇(五)——資料庫的鎖
一、資料庫的鎖機制及資料庫中有哪些鎖?
鎖是一種併發控制技術,鎖是用來在多個使用者同時訪問同一個資料的時候保護資料的。
兩種基本的鎖型別
- 共享鎖(S):多個事務科封鎖一個共享頁,任何事務都不能修改該頁;通常是該頁被讀取完畢,S鎖立即被釋放。
- 排它鎖(X):僅允許一個事務封鎖該頁,其他事務必須等X鎖釋放後才能對該頁進行訪問。
二、死鎖
產生死鎖的四個必要條件:
- 互斥條件:一個資源只能被一個程序使用
- 請求與保持條件:一個程序因請求資源阻塞是,對已獲得的資源保持不放
- 不可剝奪條件:程序已獲得的資源,在未使用完之前,不能強行剝奪
- 環路等待條件:若干程序形成一種迴圈等待資源的關係
只要系統發生了死鎖,這些條件必須滿足,只要一個條件不滿足,就不會發生死鎖。
預防死鎖:
預防死鎖只需要破壞四個必要條件之一即可。
避免死鎖:
銀行家演算法
檢測死鎖:
死鎖定理
解除死鎖:
- 從死鎖程序處剝奪資源
- 終止部分或全部程序
三、MySQL鎖的粒度(鎖的級別)
MySQL各儲存引擎使用了三種級別的鎖定機制:行級鎖定、頁級鎖定和表級鎖定。
1、表級鎖:
直接鎖定整張表。鎖定時,其他程序不能對該表進行寫操作。如果是寫鎖,其他程序則讀也不允許。
特點:
- 開銷小,加鎖快;
- 不會出現死鎖;
- 鎖定粒度最大,發生鎖衝突的概率最高,併發度最低。
MyISAM儲存引擎採用的是表級鎖
有兩種模式:
表共享讀鎖(lock table 表名 read;)和表獨佔寫鎖。去掉鎖的命令(unlock tables)
支援併發插入:支援查詢和插入操作併發進行(在表尾併發插入)
鎖排程機制:
寫鎖優先。一個程序請求某個MyISAM表的讀鎖,同時有一個程序也請求這個表的寫鎖,MySQL是讓寫鎖優先獲得鎖。
2、行級鎖:
僅對指定的記錄進行加鎖,其他程序還可以對同一個表中的其他記錄進行操作。
特點:
- 開銷大,加鎖慢;
- 會出現死鎖;
- 鎖定粒度最小,發生鎖衝突的概率最低,併發度最高
InnoDB儲存引擎即支援行級鎖,也支援表級鎖,預設情況下是採用行級鎖。
3、頁級鎖
一次鎖定相鄰的一組記錄。
特點:
- 開銷和加鎖時間介於表鎖與行鎖之間;
- 會出現死鎖;
- 鎖粒度介於表鎖與行鎖之間,併發度一般。
總結
最常用的處理多使用者併發訪問的方法是加鎖。當一個使用者鎖住資料庫中的某個物件時,其他使用者就不能再訪問該物件。加鎖隨併發訪問的影響體現在所的粒度上。比如:(表鎖)放在一個表上的鎖限制對整個表的併發訪問;(頁鎖)在資料頁的鎖限制了對整個資料頁的訪問;(行鎖)只限制對該行的併發訪問。
四、資料庫的樂觀鎖和悲觀鎖是什麼?
1、悲觀鎖:
假定會發生併發衝突,遮蔽一切可能違反資料完整性的操作。
以MySQL的鎖表過程為例:
要將select.... for update;放在MySQL事務中,即begin與commit中,否則不起作用
表結構為:
表中內容如下:
開啟兩個視窗,其中一個執行select * from ta for update;
在兩一個視窗中執行update操作:
視窗1
視窗2
在視窗1中執行commit後,視窗2中的update才執行。
特點:
悲觀鎖可能會造成加鎖的時間很長,併發性不好,特別是長事務,影響系統的整體效能。
悲觀鎖的實現方式:
基於資料庫的鎖機制實現。傳統的關係型資料庫裡用到了這種鎖機制,比如:行鎖、表鎖等,讀鎖、寫鎖等,都是在做操作之前先上鎖。
2、樂觀鎖:
假設不會發生併發衝突,只在提交操作時檢查是否違反資料完整性。
特點:
比悲觀鎖加鎖的時間短,大大提升了大併發量下的系統整體效能表現。
樂觀鎖的實現方式:
1)大多是基於資料版本記錄機制實現
需要為每一行資料增加一個版本標識(每一行增加一個欄位version),每次更新資料都要更新對應的版本號+1;
也就是說:提交的資料版本號大於資料庫當前版本號,則予以更新,否則認為是過期資料,不得不重新讀取物件並作出更改。
示例:
假設資料庫中賬戶資訊表中有一個version欄位,當前值為1,而當前賬戶餘額欄位(balance)為$100。有以下操作:
1、操作員A讀出(version=1),並將balance扣除$50($100-$50);
2、在A操作的過程中,操作員B也讀入使用者資訊(version=1),並扣除$20($100-$20);
3、A完成了修改操作,將version+1(2),balance為$50提交,由於提交版本大於資料記錄當前版本,資料被更新,此時version=2,balance=$50;
4、B完成操作哦,也將版本號加1(為2)試圖想資料庫提交資料(balance=$80),但此時比對資料庫記錄版本時發現,提交版本號不大於資料庫記錄版本號,因此,B的提交被駁回。
以上示例中發現,樂觀鎖機制避免了長事務中的資料庫加鎖開銷(A和B的操作中都沒有對資料庫資料加鎖),大大提升了大併發量下的系統整體效能表現。
2)使用時間戳來實現
同樣是在需要樂觀鎖控制的表中增加一個欄位,欄位型別是時間戳(timestamp),和上面的version類似,也是在提交更新時檢查當前資料庫中資料的時間戳和自己更新前取到的時間戳進行對比,若一致,提交成功;否則就是版本衝突。
3、悲觀鎖和樂觀鎖的適用場景
如果併發量不大,可以使用悲觀鎖解決併發問題;
若系統的併發量特別大的話,選擇樂觀鎖;
現在大部分應用都是選擇樂觀鎖。