06 MySQL-初識MySQL-鎖-全局鎖-表鎖

數據庫鎖設計的初衷是處理并發問題。作為多用戶共享的資源,當出現并發訪問的時候,數據庫需要合理地控制資源的訪問規則。而鎖就是用來實現這些訪問規則的重要數據結構。

根據加鎖的范圍,MySQL里面的鎖大致可以分成全局鎖、表級鎖和行鎖三類
本篇進行討論全局鎖和表級鎖。而關于行鎖的內容,在下一篇文章中詳細介紹。

這里需要說明的是,鎖的設計比較復雜,這兩篇文章不會涉及鎖的具體實現細節,主要介紹的是碰到鎖時的現象和其背后的原理。

一 全局鎖

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

MySQL提供了一個加 全局讀鎖 的方法,命令是 Flush tables with read lock (FTWRL), 可以用unlock tables主動釋放鎖。當你需要讓整個庫處于只讀狀態的時候,可以使用這個命令,之后其他線程的以下語句會被阻塞:數據更新語句(數據的增刪改)、數據定義語句(包括建表、修改表結構等)和更新類事務的提交語句。

  1. 全局鎖的典型使用場景是,做全庫邏輯備份。 也就是把整庫每個表都select出來存成文本。

以前有一種做法,是通過FTWRL確保不會有其他線程對數據庫做更新,然后對整個庫做備份。注意,在備份過程中整個庫完全處于只讀狀態。但是讓整庫都只讀,聽上去就很危險:

  • 如果你在主庫上備份,那么在備份期間都不能執行更新,業務基本上就得停擺;
  • 如果你在從庫上備份,那么備份期間從庫不能執行主庫同步過來的binlog,會導致主從延遲。

看來加全局鎖不太好。但是細想一下,備份為什么要加鎖呢?我們來看一下不加鎖會有什么問題。

  1. 業務場景分析:
    假設現在要維護“理財App”的購買系統,關注的是用戶賬戶余額表和用戶購買理財產品表。
  • 現在發起一個邏輯備份。假設備份期間,有一個用戶,他購買了一類理財產品,業務邏輯里就要扣掉他的余額,然后往已購產品里面加上一類理財產品。邏輯如下:
* 如果時間順序上是先備份賬戶余額表(u_account),然后用戶購買,然后備份用戶課程表(u_course),會怎么樣呢?你可以看一下這個圖:  
mysql-golable-lock-Page.png
* 結果就是這個備份結果里,用戶A的數據狀態是“賬戶余額沒扣,但是用戶理財產品表里面已經多了一產品”。如果后面用這個備份來恢復數據的話,用戶A就發現,自己賺了。

* 如果備份表的順序反過來,先備份用戶課程表再備份賬戶余額表,又可能會出現什么結果?也就是說,不加鎖的話,備份系統備份的得到的庫不是一個邏輯時間點,這個視圖是邏輯不一致的。說到視圖你肯定想起來了,我們在前面講事務隔離的時候,其實是有一個方法能夠拿到一致性視圖的,就是在可重復讀隔離級別下開啟一個事務。
  • 官方自帶的邏輯備份工具是mysqldump。當mysqldump使用參數–single-transaction的時候,導數據之前就會啟動一個事務,來確保拿到一致性視圖。而由于MVCC的支持,這個過程中數據是可以正常更新的。

  • 有了這個功能,為什么還需要FTWRL呢?一致性讀是好,但前提是引擎要支持這個隔離級別。 比如,對于MyISAM這種不支持事務的引擎,如果備份過程中有更新,總是能取到最新的數據,那么就破壞了備份的一致性。這時,我們就需要使用FTWRL命令了。所以,single-transaction方法只適用于所有的表使用事務引擎的庫。如果有的表使用了不支持事務的引擎,那么備份就只能通過FTWRL方法。這往往是DBA要求業務開發人員使用InnoDB替代MyISAM的原因之一。既然要全庫只讀,為什么不使用set global readonly=true的方式呢 ?確實readonly方式也可以讓全庫進入只讀狀態,但我還是會建議你用FTWRL方式,主要有兩個原因:

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

    • 二是,在異常處理機制上有差異。如果執行FTWRL命令之后由于客戶端發生異常斷開,那么MySQL會自動釋放這個全局鎖,整個庫回到可以正常更新的狀態。而將整個庫設置為readonly之后,如果客戶端發生異常,則數據庫就會一直保持readonly狀態,這樣會導致整個庫長時間處于不可寫狀態,風險較高。

業務的更新不只是增刪改數據(DML),還有可能是加字段等修改表結構的操作(DDL)。不論是哪種方法,一個庫被全局鎖上以后,你要對里面任何一個表做加字段操作,都是會被鎖住的。

二 表級鎖

MySQL里面表級別的鎖有兩種:一種是表鎖,一種是元數據鎖(meta data lock,MDL)。

表鎖

表鎖的語法是 lock tables … read/write。 與FTWRL類似,可以用unlock tables主動釋放鎖,也可以在客戶端斷開的時候自動釋放。需要注意,lock tables語法除了會限制別的線程的讀寫外,也限定了本線程接下來的操作對象。

舉個例子, 如果在某個線程A中執行lock tables t1 read, t2 write; 這個語句,則其他線程寫t1、讀寫t2的語句都會被阻塞。同時,線程A在執行unlock tables之前,也只能執行讀t1、讀寫t2的操作。連寫t1都不允許,自然也不能訪問其他表。在還沒有出現更細粒度的鎖的時候,表鎖是最常用的處理并發的方式。而對于InnoDB這種支持行鎖的引擎,一般不使用lock tables命令來控制并發,畢竟鎖住整個表的影響面還是太大。

MDL(metadata lock)

另一類表級的鎖是MDL(metadata lock)。 MDL不需要顯式使用,在訪問一個表的時候會被自動加上。MDL的作用是,保證讀寫的正確性。你可以想象一下,如果一個查詢正在遍歷一個表中的數據,而執行期間另一個線程對這個表結構做變更,刪了一列,那么查詢線程拿到的結果跟表結構對不上,肯定是不行的。

MySQL 5.5版本中引入了MDL,當對一個表做增刪改查操作的時候,加MDL讀鎖;當要對表做結構變更操作的時候,加MDL寫鎖。

  • 讀鎖之間不互斥,因此你可以有多個線程同時對一張表增刪改查。

  • 讀寫鎖之間、寫鎖之間是互斥的,用來保證變更表結構操作的安全性。因此,如果有兩個線程要同時給一個表加字段,其中一個要等另一個執行完才能開始執行。

場景分析:
雖然MDL鎖是系統默認會加的,但卻是你不能忽略的一個機制。比如下面這個例子,有人掉到這個坑里:給一個小表加個字段,導致整個庫掛了。你肯定知道,給一個表加字段,或者修改字段,或者加索引,需要掃描全表的數據。在對大表操作的時候,你肯定會特別小心,以免對線上服務造成影響。而實際上,即使是小表,操作不慎也會出問題。我們來看一下下面的操作序列,假設表t是一個小表。

mysql-table-block-1-Page.png
  • session A先啟動,這時候會對表t加一個MDL讀鎖。

  • 由于session B需要的也是MDL讀鎖,因此可以正常執行。

  • session C會被blocked,是因為session A的MDL讀鎖還沒有釋放,而session C需要MDL寫鎖,因此只能被阻塞。

  • 如果只有session C自己被阻塞還沒什么關系,但是之后所有要在表t上新申請 MDL讀鎖的請求 也會被session C阻塞。前面我們說了,所有對表的增刪改查操作都需要先申請MDL讀鎖,就都被鎖住,等于這個表現在完全不可讀寫了。

  • 如果某個表上的查詢語句頻繁,而且客戶端有重試機制,也就是說超時后會再起一個新session再請求的話,這個庫的線程很快就會爆滿。

現在應該知道了,事務中的MDL鎖,在語句執行開始時申請,但是語句結束后并不會馬上釋放,而會等到整個事務提交后再釋放。

基于上面的分析,我們來討論一個問題,如何安全地給小表加字段?

首先需要解決長事務,事務不提交,就會一直占著MDL鎖。在MySQL的information_schema 庫的 innodb_trx 表中,你可以查到當前執行中的事務。如果你要做DDL變更的表剛好有長事務在執行,要考慮先暫停DDL,或者kill掉這個長事務。

但考慮一下這個場景。如果你要變更的表是一個熱點表,雖然數據量不大,但是上面的請求很頻繁,而你不得不加個字段,你該怎么做呢?

這時候kill可能未必管用,因為新的請求馬上就來了。比較理想的機制是,在alter table語句里面設定等待時間,如果在這個指定的等待時間里面能夠拿到MDL寫鎖最好,拿不到也不要阻塞后面的業務語句,先放棄。之后開發人員或者DBA再通過重試命令重復這個過程。

MariaDB已經合并了AliSQL的這個功能,所以這兩個開源分支目前都支持DDL NOWAIT/WAIT n這個語法。

ALTER TABLE tbl_name NOWAIT add column ...
ALTER TABLE tbl_name WAIT N add column ... 
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,030評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,310評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,951評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,796評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,566評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,055評論 1 322
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,142評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,303評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,799評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,683評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,899評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,409評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,135評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,520評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,757評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,528評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,844評論 2 372

推薦閱讀更多精彩內容

  • ORA-00001: 違反唯一約束條件 (.) 錯誤說明:當在唯一索引所對應的列上鍵入重復值時,會觸發此異常。 O...
    我想起個好名字閱讀 5,378評論 0 9
  • 數據庫鎖設計的初衷是處理并發問題。作為多用戶共享的資源,當出現并發訪問的時候,數據庫需要合理地控制資源的訪問規則。...
    yywfy的昵稱閱讀 554評論 0 0
  • 今天看到一位朋友寫的mysql筆記總結,覺得寫的很詳細很用心,這里轉載一下,供大家參考下,也希望大家能關注他原文地...
    信仰與初衷閱讀 4,746評論 0 30
  • 摘要:一、故障描述 今天一個朋友遇到數據庫遇到一個嚴重的故障,故障環境如下: MYSQL 5.6.16 RR隔離級...
    暖夏未眠丶閱讀 701評論 0 1
  • 我覺得這東西,很快也就曇花一現。真的。 回憶了一下上次張小龍的演講,如下: 第一個問題是小程序的入口在哪里?張小龍...
    徐不同閱讀 1,661評論 21 10