數(shù)據(jù)庫鎖
鎖和索引是數(shù)據(jù)庫兩大核心概念,了解索引,可以從 B+ 樹,Hash 索引,頁結(jié)構(gòu),緩存池,索引原則等方面理解。理解鎖,要從哪些方面入手?
為什么要加鎖
加鎖的目的,其實是為了保證數(shù)據(jù)的一致性。 當(dāng)多個線程并發(fā)訪問某個數(shù)據(jù)時,加鎖,可以保證這個數(shù)據(jù)在任何時刻最多只有一個線程在訪問,保證數(shù)據(jù)的完整性和一致性。
鎖的分類
鎖可以按照鎖粒度劃分,可以按照數(shù)據(jù)庫管理角度劃分。
按照鎖粒度劃分
按照鎖粒度劃分,可以將鎖劃分成 行鎖,頁鎖和表鎖。
快速回憶一遍 InnoDB 存儲引擎的邏輯結(jié)構(gòu):所有數(shù)據(jù)都被邏輯地存放在一個空間內(nèi),稱為表空間,而表空間由段(sengment)、區(qū)(extent)、頁(page)組成。
InnoDB存儲引擎的邏輯結(jié)構(gòu)
- 行鎖
行鎖,就是按照行的粒度對數(shù)據(jù)進(jìn)行鎖定,鎖定粒度小,發(fā)生鎖沖突概率低,可以實現(xiàn)并發(fā)都高,但是對于鎖的開銷比較大,加上會比較慢,容易出現(xiàn)死鎖的情況。
- 頁鎖
頁鎖就是頁的粒度上進(jìn)行鎖定,鎖定的數(shù)據(jù)資源比行鎖要多,因為一個頁中可以有多個行記錄,當(dāng)我們使用頁鎖的時候,會出現(xiàn)數(shù)據(jù)浪費的現(xiàn)象,頁鎖的開銷介于表鎖行鎖之間。
- 表鎖
表鎖就是對數(shù)據(jù)進(jìn)行鎖定,鎖定粒度很大,發(fā)送鎖的概率很高,數(shù)據(jù)訪問的并發(fā)度。不過好處在于對鎖的使用開銷小,加鎖會很快。
InnoDB 和 Oracle 支持行鎖和表鎖,MyISAM 只支持表鎖, MySQL BDB 存儲引擎支持頁鎖和表鎖。SQL Server 可以支持行鎖,頁鎖和表鎖。
image
按照數(shù)據(jù)庫管理角度劃分
按照數(shù)據(jù)庫管理角度劃分,可以將鎖分成排他鎖和共享鎖。
- 共享鎖
共享鎖,也叫讀鎖,或者 S 鎖,共享鎖鎖定的資源可以被其他用戶讀取,但不能修改。 在進(jìn)行 SElECT 的時候,會將對象進(jìn)行共享鎖鎖定,當(dāng)數(shù)據(jù)讀取完畢之后,就會釋放共享鎖,這樣就可以保證數(shù)據(jù)在讀取時不被修改。
給某個表加共享鎖:
LOCK TABLE product_comment READ;
當(dāng)數(shù)據(jù)表加上共享鎖的時候,該表數(shù)據(jù)就會變成只讀模式,當(dāng)時我們想更新 product_comment 表中的數(shù)據(jù)會報錯,比如:
UPDATE product_comment SET product_id = 10002 WHERE user_id = 912178;
系統(tǒng)報錯如下:
ERROR 1099 (HY000): Table 'product_comment' was locked with a READ lock and can't be updated
如果對表共享鎖進(jìn)行解鎖:
UNLOCK TABLE;
給某行數(shù)據(jù)加共享鎖
SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE
例子:
SELECT comment_id, product_id, comment_text, user_id FROM product_comment WHERE user_id = 912178 LOCK IN SHARE MODE
- 排他鎖
排他鎖也叫做獨占鎖,寫鎖或者 X 鎖,排他鎖鎖定的數(shù)據(jù)只允許進(jìn)行鎖定操作的事務(wù)使用,其他事務(wù)無法對已鎖定的數(shù)據(jù)進(jìn)行查詢或者修改。
給表加排他鎖
LOCK TABLE product_comment WRITE;
排他鎖的事務(wù)可以對 product_comment 進(jìn)行查詢和修改。其他事務(wù)如果想要在 product_comment 表上查詢數(shù)據(jù),則需要等待。
釋放掉排他鎖
UNLOCK TABLE;
數(shù)據(jù)行上添加排他鎖
SELECT * FROM table_name WHERE ... FOR UPDATE
例子:
SELECT comment_id, product_id, comment_text, user_id FROM product_comment WHERE user_id = 912178 FOR UPDATE;
當(dāng)我們對數(shù)據(jù)進(jìn)行更新的時候會自動使用排他鎖,也就是 INSERT ,DELETE 或者 UPDATE 的時候,數(shù)據(jù)庫自動使用排他鎖,防止其他事務(wù)對改數(shù)據(jù)進(jìn)行操作。
從程序員角度進(jìn)行劃分
- 樂觀鎖
樂觀鎖認(rèn)為對同一個數(shù)據(jù)并發(fā)操作不會總發(fā)生,是小概率事件,因此不用每次對數(shù)據(jù)進(jìn)行更新或者刪除。
UPDATE ... SET version=version+1 WHERE version=version
此時,如果已經(jīng)有事務(wù)對這條數(shù)據(jù)進(jìn)行了更新,修改就不會成功。
- 悲觀鎖
悲觀鎖(Pessimistic Locking),通過數(shù)據(jù)庫自身的鎖機(jī)制來實現(xiàn),從而保證數(shù)據(jù)操作的排他性。
- 樂觀鎖適合讀操作多的場景,相對來說寫的操作?較少。它的優(yōu)點在于程序?qū)崿F(xiàn),不存在死鎖問題,不過適?場景也會相對樂觀,因為它阻?不了除了程序以外的數(shù)據(jù)庫操作。
- 悲觀鎖適合寫操作多的場景,因為寫的操作具有排它性。采?悲觀鎖的?式,可以在數(shù)據(jù)庫層?阻?其他事務(wù)對該數(shù)據(jù)的操作權(quán)限,防?讀-寫和寫-寫的沖突