数据库的封锁
封鎖
封鎖粒度
MySQL 中提供了兩種封鎖粒度:行級(jí)鎖以及表級(jí)鎖。(還有一種折中的鎖叫 頁級(jí)鎖 BDB支持)
應(yīng)該盡量只鎖定需要修改的那部分?jǐn)?shù)據(jù),而不是所有的資源。鎖定的數(shù)據(jù)量越少,發(fā)生鎖爭用的可能就越小,系統(tǒng)的并發(fā)程度就越高。
但是加鎖需要消耗資源,鎖的各種操作(包括獲取鎖、釋放鎖、以及檢查鎖狀態(tài))都會(huì)增加系統(tǒng)開銷。因此封鎖粒度越小,系統(tǒng)開銷就越大。
在選擇封鎖粒度時(shí),需要在鎖開銷和并發(fā)程度之間做一個(gè)權(quán)衡。
?
封鎖類型
1. 讀寫鎖
- 排它鎖(Exclusive),簡寫為 X 鎖,又稱寫鎖。
- 共享鎖(Shared),簡寫為 S 鎖,又稱讀鎖。
有以下兩個(gè)規(guī)定:
- 一個(gè)事務(wù)對(duì)數(shù)據(jù)對(duì)象 A 加了 X 鎖,就可以對(duì) A 進(jìn)行讀取和更新。加鎖期間其它事務(wù)不能對(duì) A 加任何鎖,即其他事務(wù)不能讀,不能寫。
- 一個(gè)事務(wù)對(duì)數(shù)據(jù)對(duì)象 A 加了 S 鎖,可以對(duì) A 進(jìn)行讀取操作,但是不能進(jìn)行更新操作。加鎖期間其它事務(wù)能對(duì) A 加 S 鎖,但是不能加 X 鎖。
鎖的兼容關(guān)系如下:
| X | × | × |
| S | × | √ |
2. 意向鎖
使用意向鎖(Intention Locks)可以更容易地支持多粒度封鎖。
在存在行級(jí)鎖和表級(jí)鎖的情況下,事務(wù) T 想要對(duì)表 A 加 X 鎖,就需要先檢測是否有其它事務(wù)對(duì)表 A 或者表 A 中的任意一行加了鎖,那么就需要對(duì)表 A 的每一行都檢測一次,這是非常耗時(shí)的。
意向鎖在原來的 X/S 鎖之上引入了 IX/IS,IX/IS 都是表鎖,用來表示一個(gè)事務(wù)想要在表中的某個(gè)數(shù)據(jù)行上加 X 鎖或 S 鎖。有以下兩個(gè)規(guī)定:
- 一個(gè)事務(wù)在獲得某個(gè)數(shù)據(jù)行對(duì)象的 S 鎖之前,必須先獲得表的 IS 鎖或者更強(qiáng)的鎖;
- 一個(gè)事務(wù)在獲得某個(gè)數(shù)據(jù)行對(duì)象的 X 鎖之前,必須先獲得表的 IX 鎖。
通過引入意向鎖,事務(wù) T 想要對(duì)表 A 加 X 鎖,只需要先檢測是否有其它事務(wù)對(duì)表 A 加了 X/IX/S/IS 鎖,如果加了就表示有其它事務(wù)正在使用這個(gè)表或者表中某一行的鎖,因此事務(wù) T 加 X 鎖失敗。
各種鎖的兼容關(guān)系如下:
| X | × | × | × | × |
| IX | × | √ | × | √ |
| S | × | × | √ | √ |
| IS | × | √ | √ | √ |
解釋如下:
- 任意 IS/IX 鎖之間都是兼容的,因?yàn)樗鼈冎皇潜硎鞠胍獙?duì)表加鎖,而不是真正加鎖;
- S 鎖只與 S 鎖和 IS 鎖兼容,也就是說事務(wù) T 想要對(duì)數(shù)據(jù)行加 S 鎖,其它事務(wù)可以已經(jīng)獲得對(duì)表或者表中的行的 S 鎖。
封鎖協(xié)議
1. 三級(jí)封鎖協(xié)議
一級(jí)封鎖協(xié)議
事務(wù) T 要修改數(shù)據(jù) A 時(shí)必須加 X 鎖,直到 T 結(jié)束才釋放鎖。
可以解決丟失修改問題,因?yàn)椴荒芡瑫r(shí)有兩個(gè)事務(wù)對(duì)同一個(gè)數(shù)據(jù)進(jìn)行修改,那么事務(wù)的修改就不會(huì)被覆蓋。
| lock-x(A) | ? |
| read A=20 | ? |
| ? | lock-x(A) |
| ? | wait |
| write A=19 | . |
| commit | . |
| unlock-x(A) | . |
| ? | obtain |
| ? | read A=19 |
| ? | write A=21 |
| ? | commit |
| ? | unlock-x(A) |
二級(jí)封鎖協(xié)議
在一級(jí)的基礎(chǔ)上,要求讀取數(shù)據(jù) A 時(shí)必須加 S 鎖,讀取完馬上釋放 S 鎖。
可以解決讀臟數(shù)據(jù)問題,因?yàn)槿绻粋€(gè)事務(wù)在對(duì)數(shù)據(jù) A 進(jìn)行修改,根據(jù) 1 級(jí)封鎖協(xié)議,會(huì)加 X 鎖,那么就不能再加 S 鎖了,也就是不會(huì)讀入數(shù)據(jù)。
| lock-x(A) | ? |
| read A=20 | ? |
| write A=19 | ? |
| ? | lock-s(A) |
| ? | wait |
| rollback | . |
| A=20 | . |
| unlock-x(A) | . |
| ? | obtain |
| ? | read A=20 |
| ? | unlock-s(A) |
| ? | commit |
三級(jí)封鎖協(xié)議
在二級(jí)的基礎(chǔ)上,要求讀取數(shù)據(jù) A 時(shí)必須加 S 鎖,直到事務(wù)結(jié)束了才能釋放 S 鎖。
可以解決不可重復(fù)讀的問題,因?yàn)樽x A 時(shí),其它事務(wù)不能對(duì) A 加 X 鎖,從而避免了在讀的期間數(shù)據(jù)發(fā)生改變。
| lock-s(A) | ? |
| read A=20 | ? |
| ? | lock-x(A) |
| ? | wait |
| read A=20 | . |
| commit | . |
| unlock-s(A) | . |
| ? | obtain |
| ? | read A=20 |
| ? | write A=19 |
| ? | commit |
| ? | unlock-X(A) |
2. 兩段鎖協(xié)議
加鎖和解鎖分為兩個(gè)階段進(jìn)行。
可串行化調(diào)度是指,通過并發(fā)控制,使得并發(fā)執(zhí)行的事務(wù)結(jié)果與某個(gè)串行執(zhí)行的事務(wù)結(jié)果相同。
事務(wù)遵循兩段鎖協(xié)議是保證可串行化調(diào)度的充分條件。例如以下操作滿足兩段鎖協(xié)議,它是可串行化調(diào)度。
lock-x(A)...lock-s(B)...lock-s(C)...unlock(A)...unlock(C)...unlock(B)但不是必要條件,例如以下操作不滿足兩段鎖協(xié)議,但是它還是可串行化調(diào)度。
lock-x(A)...unlock(A)...lock-s(B)...unlock(B)...lock-s(C)...unlock(C)MySQL 隱式與顯示鎖定
MySQL 的 InnoDB 存儲(chǔ)引擎采用兩段鎖協(xié)議,會(huì)根據(jù)隔離級(jí)別在需要的時(shí)候自動(dòng)加鎖,并且所有的鎖都是在同一時(shí)刻被釋放,這被稱為隱式鎖定。
InnoDB 也可以使用特定的語句進(jìn)行顯示鎖定:
SELECT ... LOCK In SHARE MODE; SELECT ... FOR UPDATE;?當(dāng)問到數(shù)據(jù)庫中的鎖機(jī)制的時(shí)候?
從封鎖的粒度、類型、協(xié)議!!!結(jié)合不同的數(shù)據(jù)庫引擎來說!
我們可以這樣說:
1,數(shù)據(jù)庫中的鎖機(jī)制,從鎖的粒度來劃分,分為行級(jí)鎖和表級(jí)鎖(定義,開銷,鎖爭用情況,并發(fā)程度)。
MYISAM支持表級(jí)鎖,INNODB既支持表級(jí)鎖,又支持行級(jí)鎖,默認(rèn)行級(jí)鎖
2,從鎖的類型劃分分為:讀寫鎖(X鎖和S鎖-------->(定義,規(guī)則))和意向鎖(為什么有意向鎖?每一次加鎖都需要查詢這個(gè)資源是否被加鎖,而這一個(gè)過程很耗時(shí),所以添加意向鎖來表示我即將要對(duì)某一個(gè)資源進(jìn)行加鎖操作!那我們要對(duì)某一資源進(jìn)行加鎖時(shí)只需要看有沒有加意向鎖!)
?3,封鎖協(xié)議:三級(jí)鎖協(xié)議+兩段鎖協(xié)議
4,數(shù)據(jù)庫的死鎖問題:
行級(jí)鎖與死鎖
MyISAM中是不會(huì)產(chǎn)生死鎖的,因?yàn)镸yISAM總是一次性獲得所需的全部鎖(表級(jí)鎖),要么全部滿足,要么全部等待。而在InnoDB中,鎖是逐步獲得的,就造成了死鎖的可能。
在MySQL中,行級(jí)鎖并不是直接鎖記錄,而是鎖索引。索引分為主鍵索引和非主鍵索引兩種,如果一條sql語句操作了主鍵索引,MySQL就會(huì)鎖定這條主鍵索引;如果一條語句操作了非主鍵索引,MySQL會(huì)先鎖定該非主鍵索引,再鎖定相關(guān)的主鍵索引。 在UPDATE、DELETE操作時(shí),MySQL不僅鎖定WHERE條件掃描過的所有索引記錄,而且會(huì)鎖定相鄰的鍵值,即所謂的next-key locking(間隙鎖)。
當(dāng)兩個(gè)事務(wù)同時(shí)執(zhí)行,一個(gè)鎖住了主鍵索引,在等待其他相關(guān)索引。另一個(gè)鎖定了非主鍵索引,在等待主鍵索引。這樣就會(huì)發(fā)生死鎖。
發(fā)生死鎖后,InnoDB一般都可以檢測到,并使一個(gè)事務(wù)釋放鎖回退,另一個(gè)獲取鎖完成事務(wù)。
有多種方法可以避免死鎖,這里只介紹常見的三種
1、如果不同程序會(huì)并發(fā)存取多個(gè)表,盡量約定以相同的順序訪問表,可以大大降低死鎖機(jī)會(huì)。
2、在同一個(gè)事務(wù)中,盡可能做到一次鎖定所需要的所有資源,減少死鎖產(chǎn)生概率;
3、對(duì)于非常容易產(chǎn)生死鎖的業(yè)務(wù)部分,可以嘗試使用升級(jí)鎖定顆粒度,通過表級(jí)鎖定來減少死鎖產(chǎn)生的概率;
下一篇博文講一下樂觀鎖和悲觀鎖
總結(jié)
- 上一篇: 数据库的隔离级别
- 下一篇: 数据库的事务隔离技术 之 MVCC