全域性鎖和表鎖 給表加個字段怎麼有這麼多阻礙?

2021-09-25 15:17:21 字數 2712 閱讀 9832

不論是我們平時接觸到的synchronized還是reentrantlock等等鎖,都是為了解決業務中併發的問題,而資料庫中也是如此,mysql裡面的鎖大致分為三種,全域性鎖,表級鎖和行鎖三類。今天這篇文章,將會和你分享全域性鎖和表級鎖。

鎖的設計是很複雜的,專欄會主要圍繞碰到鎖的現象和其背後的原理。

全域性鎖就是整個資料庫例項枷鎖。mysql提供了乙個加全域性讀鎖的方法,命令是flush tables with read lock。當你需要讓整個庫處於唯讀狀態的時候,就可以使用這個命令,之後其他執行緒的以下語句就會被阻塞。資料的增刪改,create表結構和update表結構,還有事務類的提交語句

全域性鎖的使用場景是:全庫邏輯備份。也就是把每個表select出來存成文字。

以前有一種做法,是通過ftwrl確保不會有其他執行緒對資料庫做更新,然後對整個庫做備份。當然,做備份的時候,就是唯讀狀態。

但是讓整個資料庫都唯讀,是很危險的:

那麼,我們又為什麼要加全域性鎖呢?

如果我們要維護乙個購買系統,那麼關注的就是使用者餘額表和使用者課程表,現在發起乙個邏輯備份,假設備份期間,有乙個使用者,購買了東西,那麼業務邏輯就要扣掉餘額,然後給購買物品。

如果從時間順序上是先備份賬戶餘額表(user_account),然後使用者購買,然後備份使用者購買物品表(user_sth)會如何呢?

在備份的結果中會顯示,餘額不變,但是物品+1。這是因為備份系統備份到的庫不在乙個邏輯時間點,檢視邏輯不一致

而獲取到試圖邏輯一致的方法是在可重複讀隔離級別下開啟乙個事務。

官方在這方面自帶了乙個工具,是mysqldump,當使用引數-single-transaction的時候,導資料之前就會啟動乙個事務,來保證獲得一致性邏輯檢視,由於支援mvcc,這個過程中資料是可以正常更新的。

那麼既然我們可以使用可重複讀來避免檢視邏輯不一致,那我們還為啥要用全域性鎖呢?一致性讀是好,但是前提是引擎要支援這個隔離級別,比如my了sam就不支援,那麼就得使用全域性鎖了。

所以,我們才更喜歡用innodb啊。

既然全庫唯讀,那麼為啥不使用set global readonly = true呢?畢竟也可以讓資料庫進入唯讀狀態。好,原因如下;

在有些系統中,readonly的值會被用來做其他邏輯,比如用來判斷乙個庫是主庫還是備庫。因此,修改global變數的方式影響更大,不建議使用。

異常處理機制上,使用全域性鎖遇見問題會中斷,然後釋放全域性鎖。但是設定readonly,遇見問題會停滯,導致資料庫處於長期不可寫的狀態。

業務的更新包括兩方面:dml(增刪改資料),ddl(加欄位,修改建立表結構)。不論是哪種行為,乙個庫被全域性鎖上以後,要對任何乙個表加字段都會被鎖住。即使沒有被鎖,過程也很簡單,所以就來介紹乙個表級鎖。

mysql裡面的表級別鎖有兩種:一是表鎖,二是元資料鎖

表鎖表鎖的語法是lock tables …read/write。與全域性鎖語法類似,可以用unlock tables主動釋放鎖,也可以在client端斷開的時候自動釋放。需要很在意的是,lock tables語法除了會限制別的執行緒讀寫之外,也限定了本縣城接下來的操作物件。

在沒有出現粒度更細的鎖之前,表鎖是最常用的處理併發的方式。但是對於innodb這種支援行鎖的引擎,一般不適用lock tables命令來控制併發,畢竟鎖住整個表的影響面還是太大。

元資料鎖

metadata lock,簡稱mdl,mdk不需要顯示使用,在訪問乙個表的時候就自動加上了,作用是保證讀寫的正確性,防止進行髒讀,這是很重要的。

因此,黨對乙個表進行crud的時候加mdl讀鎖,當對表結構進行修改的時候,加mdl寫鎖。

但是mdl也是有坑的

當我們給乙個表加字段,修改字段或者加索引的時候,需要掃瞄整張表。而不管這張表是大表還是小表,都會產生一定的問題,假設t是乙個小表。

我們可以看出來是session a先啟動,這時候會對錶t加乙個mdl讀鎖。由於session b需要的也是mdl讀鎖,因此可以正常執行。

之後session c會被阻塞,因為sessiona的mdl讀鎖還沒有釋放。

如果只有sessionc被阻塞還可以,但是之後在表t上新申請乙個mdl讀鎖的請求也會被sessionc阻塞。前面還說了,所有對錶的增刪改查操作都需要先申請mdl讀鎖,那麼現在就會成為完全無法讀寫狀態。

如果某個表上的查詢語句頻繁,而且客戶端有重試機制,那麼超時後會再起乙個新session,但是又會被阻塞,就會堆疊溢位。

那麼我們現在應該也知道了,mdl鎖是等待事務提交之後才會被釋放的

那麼,我們如何給小表安全的加字段呢?

首先我們需要解決長事務,事務不提交,就會一直佔著mdl鎖,在mysql的information_schema庫的innodb_trx表中,可以查詢到當前執行的事務。如果要做ddl任務,可以先kill這個長事務。但是如果變更的表是乙個熱點表,雖然資料量不大,但是上面的請求很頻繁,我們該怎麼做呢?

因為請求很頻繁,所以理想的機制是,在alter table語句中新增等待時間,希望在這個等待時間裡面拿到mdl鎖,拿不到也不能對後面的事務進行阻塞。

全域性鎖和表鎖 給表加個字段怎麼有這麼多阻礙?

根據加鎖的範圍,mysql 裡面的鎖大致可以分成全域性鎖 表級鎖和行鎖三類。全域性鎖 官方自帶的邏輯備份工具是 mysqldump 有乙個方法能夠拿到一致性檢視。在可重複讀隔離級別下開啟乙個事務。當 mysqldump 使用引數 single transaction 的時候,導資料之前就會啟動乙個事...

表鎖和全域性鎖

目錄 鎖的作用 處理併發問題 鎖的分類 全域性鎖表級鎖 行鎖 命令 flush tables with read lock ftwrl 這個庫處理唯讀狀態 全庫邏輯備份問題 1 主庫備份,業務停擺 2 從庫備份,不能執行binlog,導致主從延遲 在不支援事物的引擎下可以使用 有事務機制的備份 my...

全域性鎖 表鎖和行鎖

全域性鎖就是對整個資料庫例項加鎖,mysql提供了乙個加全域性讀鎖的方法,命令是flush tables with read lock。讓你需要讓整個庫處於唯讀狀態的時候,可以使用這個命令,之後其他執行緒的以下語句會被阻塞 a 資料更新語句 b 資料定義語句 c 更新類事務的提交語句 mysql裡面...