說透MySQL裏的各種鎖(上篇)

0.前言

MySQL按照加鎖的範圍,分爲全局鎖、表級鎖、行級鎖。

本文作爲上篇,主要介紹MySQL的全局鎖 和 表級鎖。

重要的實戰總結爲,如何安全地變更一個表的表結構。
在這裏插入圖片描述

1.全局鎖

定義:

全局鎖就是對整個數據庫實例加鎖。

全局鎖語法:

Flush tables with read lock (FTWRL)

當你使用這個命令後,整個庫處於只讀狀態,之後其他線程的數據更新語句(DML)、數據定義語句(DDL)都會被阻塞。

場景:
不支持事務的引擎(如MyISAM),做全庫的邏輯備份。

不過我們一般使用innodb,這個鎖不太會接觸,就不展開詳細介紹了。

2.表級鎖

表級鎖其實有兩種,一種叫表鎖,一種叫原數據鎖(meta data lock, MDL)

2.1 表鎖

表鎖的語法是:

鎖表:

lock tables … read/write

釋放鎖:

unlock tables 主動釋放鎖

表鎖也能夠在客戶端斷開的時候自動釋放。

值得注意的是,lock tables 除了會限制別的線程的讀寫外,當前線程接下來的操作也會被限制。

同樣的,對於innoDB這種支持行鎖的引擎,我們一般也不會去使用用表鎖,因此,這部分內容也只需要簡單瞭解下即可。

2.2 MDL鎖

原數據鎖meta data lock 也是一種表級鎖,在 MySQL 5.5 版本中引入。

當我們對一個表做CRUD操作的時候,會加 MDL 讀鎖;當要對錶做結構變更操作的時候,加 MDL 寫鎖。
MDL讀寫鎖互斥

讀鎖之間不互斥,所以我們可以有多個線程同時對一張表增刪改查。
讀鎖和寫鎖之間、寫鎖和寫鎖之間是互斥的,用來保證變更表結構操作的安全性。因此,只要有一個線程要對一個表加字段,那天其他線程必須等這個線程完成表結構變更以後才能執行。
看到這裏,不得不給大家舉例一個日常會踩的坑。

對於大表DDL,大家一般比較謹慎,而對於小表,就會比較隨意。但是小表DDL也能造成數據庫崩潰。
表結構變更阻塞實例
Session A第一個啓動,開啓事務,select後沒有立刻commit,模仿一個長事務,此時,session A對錶tt的MDL讀鎖還沒有釋放。

Session B之後執行一個select,由於MDL讀鎖之間不互斥,因此執行成功。

Session C之後想對錶tt 執行一個alter語句,需要獲得MDL寫鎖,但是由於Session A持有了MDL讀鎖,讀寫鎖互斥,因此Session C被阻塞。

Session D這時候也想執行一個select,由於Session C被阻塞,所以Session D也會被阻塞。

所以這個時候,後續的線程就都無法讀寫了。

如果接下來這個表上有頻繁的查詢,而且客戶端有重試機制,那麼超時後會再起一個新的session來查詢,很快數據庫的線程就溢出了。

這裏有幾個小問題需要稍微再解釋一下:

  • 從Session A的情況我們得知,事務中 的MDL鎖,在語句開始時申請,但是並不是在語句結束後馬上釋放的,而是在整個事務提交後纔會釋放。
  • SessionC(DDL操作)被前面的SessionA和B(查詢操作,獲取MDL 讀鎖)所阻塞,這裏實際上並沒有成功獲取MDL寫鎖,爲什麼Session D的讀操作會被sessionC所阻塞呢?這裏的原因是,MySQL Server端,對於Session C和Session D會有一個隊列來決定誰先執行。

看到這裏,我相信肯定有對MySQL比較熟悉的朋友會問了,MySQL 5.6不是號稱支持Online DDL嗎?怎麼這裏又會有各種阻塞呢?

首先,我們先明確下什麼叫做Online DDL。

Online DDL的過程中,對於鎖的獲取分爲五步(具體online DDL過程比較複雜,本文不展開說明):

1)拿到MDL寫鎖

2)降級成MDL讀鎖

3)真正做DDL

4)升級成MDL寫鎖

5)釋放MDL鎖

1、2、4、5如果沒有鎖衝突,執行時間都是非常短的。絕大部分時間是第三步佔用了,而這個期間,表可用正常讀寫,所以被稱爲Online DDL。

而我們上文的例子,實際上是在第一步就阻塞了。

3.So,如何做一次安全的表結構變更

其實,無論大表小表的表結構變更,都應用引起我們重視。

總結了上文,大家應該都知道了最關鍵的三點:

避免長事務!

避免長事務!

避免長事務!

尤其是在執行表結構變更前,可以在information_schema庫的innodb_trx表中,查看當前執行的事務。如果有長事務正在執行,要延遲等待執行變更,或者手動先kill長事務。

另外,儘量保證表結構變更在數據庫流量低峯期操作,比如夜間,這樣能更好地避免出現風險。

所以,如何做一次安全的表結構變更?

1)避免長事務

2)在流量低峯進行

就是這麼簡單。

下一期,我們就好好聊聊 行鎖,不見不散~

參考:

丁奇《MySQL 實戰45講》

【MySQL系列相關】

1.聊一聊關於MySQL的count(*)

2.爲什麼MySQL分庫分表後總存儲大小變大了?

看到這裏了,原創不易,點個贊吧,你最好看了~

掃碼關注我的公衆號“阿丸筆記”,第一時間獲取最新更新。同時可以免費獲取海量Java技術棧電子書、各個大廠面試題。
阿丸筆記

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