《MySQL實戰45講》讀後感 06|全局鎖和表鎖:給表加個字段怎麼有這麼多阻礙

收穫到的知識點

MySQL裏面的鎖大致可以分成全局鎖、表級鎖和行鎖三類

全局鎖

加全局鎖的方法一

flush tables with read lock (FTWRL)

當你需要整個庫處於只讀狀態的時候,可以使用這個命令,之後其它的線程會被阻塞:比如dml(數據的增刪改)、ddl(建表、修改表結構)

全局鎖的使用場景

做全局邏輯備份,也就是把整個庫每個表都select出來存成文本

全局鎖後整庫只讀的風險點

如果在主庫上備份,那麼備份期間都不能進行更新操作,業務基本上就得罷工
如果在從庫上備份,那麼備份期間從庫不能執行主庫同步過來的binlog,會導致主從延遲

全局鎖既然存在風險,那麼備份時一定要加全局鎖嗎?

假設你現在要維護“極客時間”的購買系統,關注的是用戶賬戶餘額表和用戶課程表。
現在發起一個邏輯備份。假設備份期間,有一個用戶,他購買了一門課程,業務邏輯裏就要扣掉他的餘額,然後往已購課程裏面加上一門課。
如果時間順序上是先備份賬戶餘額表 (u_account),然後用戶購買,然後備份用戶課程表 (u_course),會怎麼樣呢?你可以看一下這個圖:
在這裏插入圖片描述

可以看到,這個備份結果裏,用戶 A 的數據狀態是“賬戶餘額沒扣,但是用戶課程表裏面已經多了一門課”。如果後面用這個備份來恢復數據的話,用戶 A 就發現,自己賺了。
作爲用戶可別覺得這樣可真好啊,你可以試想一下:如果備份表的順序反過來,先備份用戶課程表再備份賬戶餘額表,又可能會出現什麼結果?
也就是說,不加鎖的話,備份系統備份的得到的庫不是一個邏輯時間點,這個視圖是邏輯不一致的。

官方自帶的邏輯備份工具mysqldump,當mysqldump使用參數–single-transaction的時候,導數據之前就會啓動一個事務,來確保拿到一致性視圖,而由於MVCC的支持,這個過程中數據是可以正常更新的。

Q: 既然官方自帶的邏輯備份工具mysqldump可以解決備份時的數據一致性問題,爲什麼還需要flush tables with read lock (FTWRL)
A:這種方式雖好,但前提是引擎要支持這個隔離級別,比如MyISAM這種不支持事務的引擎,如果備份過程中有更新,總是隻能取到最新的數據,那麼就破壞備份的一致性,這時就需要使用FTWRL了

加全局鎖的方法二

set global readlonly=true

這種方式也可以讓全庫進入只讀狀態,但是作者還是建議大家使用FTWRL,主要有以下2個原因:

  • 有些系統中,readonly的值會被用來判斷其它邏輯,比如用來判斷一個庫是主庫還是備庫,因此,修改global變量的方式影響面更大
  • 在異常處理機制上有差異,如果執行FTWRL命令之後由於客戶端發生異常斷開,那麼MySQL會自動釋放這個全局鎖,而將整個庫設置爲readonly之後,如果客戶端發生異常,則數據庫就會一直保持readonly狀態,這樣會導致整個庫長時間處於不可寫狀態,風險較高

表級鎖

MySQL裏面表級鎖有2種:表鎖和元數據鎖(meta data lock,MDL)

加表鎖的方法

語法格式:lock tables ... read/write 
實際示例:lock tables t1 read , t2 write;

與FTWRL類似,可以用unlock tables主動釋放鎖,也可以在客戶端斷開的時候自動釋放。

加元數據鎖的方法

元數據鎖(meta data lock,MDL)不需要顯式使用,在訪問一個表的時候會被自動加上。MDL的作用是,保證讀寫的正確性。
MySQL5.5版本中想入了MDL,當對一個表做增刪改查的時候,加MDL讀鎖; 當對一個表做結構變更操作的時候,加MDL寫鎖。

  • MDL讀鎖不互斥,多個線程間可以對同一個表進行增刪改查
  • MDL讀鎖與寫鎖之間是互斥的, MDL寫鎖與寫鎖之間也是互斥的,因此當兩個線程要同時對一張表加字段時,其中一個需要等另一個執行完才能開始執行。

課堂問題

Q: 當備庫用–single-transaction 做邏輯備份的時候,如果從主庫的 binlog 傳來一個 DDL 語句會怎麼樣?

A: 作者回復
假設這個 DDL 是針對表 t1 的, 這裏我把備份過程中幾個關鍵的語句列出來:

Q1:SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
Q2:START TRANSACTION WITH CONSISTENT SNAPSHOT;
/* other tables /
Q3:SAVEPOINT sp;
/
時刻 1 /
Q4:show create table t1;
/
時刻 2 /
Q5:SELECT * FROM t1;
/
時刻 3 /
Q6:ROLLBACK TO SAVEPOINT sp;
/
時刻 4 /
/
other tables */

在備份開始的時候,爲了確保 RR(可重複讀)隔離級別,再設置一次 RR 隔離級別 (Q1);
啓動事務,這裏用 WITH CONSISTENT SNAPSHOT 確保這個語句執行完就可以得到一個一致性視圖(Q2);
設置一個保存點,這個很重要(Q3);
show create 是爲了拿到表結構 (Q4),然後正式導數據 (Q5),回滾到 SAVEPOINT sp,在這裏的作用是釋放 t1 的 MDL 鎖 (Q6。當然這部分屬於“超綱”,上文正文裏面都沒提到。
DDL 從主庫傳過來的時間按照效果不同,我打了四個時刻。題目設定爲小表,我們假定到達後,如果開始執行,則很快能夠執行完成。

參考答案如下:
如果在 Q4 語句執行之前到達,現象:沒有影響,備份拿到的是 DDL 後的表結構。
如果在“時刻 2”到達,則表結構被改過,Q5 執行的時候,報 Table definition has changed, please retry transaction,現象:mysqldump 終止;
如果在“時刻 2”和“時刻 3”之間到達,mysqldump 佔着 t1 的 MDL 讀鎖,binlog 被阻塞,現象:主從延遲,直到 Q6 執行完成。
從“時刻 4”開始,mysqldump 釋放了 MDL 讀鎖,現象:沒有影響,備份拿到的是 DDL 前的表結構。

順便分享一下林奇大師的課程,有興趣的可以看看

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章